0
votes

I was able to successfully run this configuration using the sample spring boot Groovy Maven implementation from https://github.com/dsyer/spring-security-angular/blob/master/vanilla/README.md. It is not working when the application code was implemented in Java and Gradle 2.3. The OPTIONS response has a new X-Auth-Token in that case.

Tried using the provided maven build with my java classes and still getting same OPTIONS 401 unauthorized response. So it's not a Gradle issue.

Copied the two ResourceApplication Groovy classes into my Gradle build and Angular ui successfully gets OPTIONS 200 OK. So there is an issue for Spring CORS filter in java.

I have the Gradle plugins for java and groovy, sourceCompatibility and targetCompatibility = 1.7

java version "1.8.0_31" Java(TM) SE Runtime Environment (build 1.8.0_31-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)

Developer console log verifies that same token was sent from ui server and received by angular client, but there is a reported CORS error.

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:9000/. This can be fixed by moving the resource to the same domain or enabling CORS. localhost:9000

@SpringBootApplication
@RestController
@EnableRedisHttpSession
public class AngularDemoApplication {
@RequestMapping("/user")
public Principal user(Principal user) {
  return user;
}

@RequestMapping("/token")
@ResponseBody
public Map<String,String> token(HttpSession session) {
  logger.debug("********** TOKEN *********** = "+session.getId());
  return Collections.singletonMap("token", session.getId());
}

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
      http.httpBasic().and().logout().and().authorizeRequests()
        .antMatchers("/index.html", "/home.html", "/login.html", "/").permitAll()
        .anyRequest().authenticated().and().csrf().csrfTokenRepository(csrfTokenRepository())
        .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
  }

  private Filter csrfHeaderFilter() {
        return new OncePerRequestFilter() {
            @Override
            protected void doFilterInternal(HttpServletRequest request,
                    HttpServletResponse response, FilterChain filterChain)
                    throws ServletException, IOException {
                CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
                        .getName());
                if (csrf != null) {
                    Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
                    String token = csrf.getToken();
                    if (cookie == null || token != null
                            && !token.equals(cookie.getValue())) {
                        cookie = new Cookie("XSRF-TOKEN", token);
                        cookie.setPath("/");
                        response.addCookie(cookie);
                    }
                }
                filterChain.doFilter(request, response);
            }
        };
    }


  private CsrfTokenRepository csrfTokenRepository() {
      HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
      repository.setHeaderName("X-XSRF-TOKEN");
      return repository;
  }
}

public static void main(String[] args) {
    SpringApplication.run(AngularDemoApplication.class, args);
}

}

@SpringBootApplication
@RestController
@EnableRedisHttpSession
public class ResourceApplication {

@RequestMapping("/")
public Map<String,Object> home() {
      Map<String,Object> model = new HashMap<String,Object>();
      model.put("id", UUID.randomUUID().toString());
      model.put("content", "Hello World");
      return model;
}


@Bean
public HeaderHttpSessionStrategy sessionStrategy() {
    return new HeaderHttpSessionStrategy();
 }

public static void main(String[] args) {
    SpringApplication.run(ResourceApplication.class, args);
}

}

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
class CorsFilter implements Filter {

@Override
public void doFilter(ServletRequest req, ServletResponse res,
        FilterChain chain) throws IOException, ServletException {
    HttpServletResponse response = (HttpServletResponse) res;
    HttpServletRequest request = (HttpServletRequest) req;
    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Headers", "x-auth-token, x-requested-with");
    if (request.getMethod() != "OPTIONS" ) {
        chain.doFilter(req, res);
    } else {
         }

}

@Override
public void init(FilterConfig filterConfig) throws ServletException {
    // TODO Auto-generated method stub

}

@Override
public void destroy() {
    // TODO Auto-generated method stub

}

}

Groovy Version:

Request Method: OPTIONS
Status code:200 OK

Request Headers:
Host: localhost:9000
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:36.0) Gecko/20100101 Firefox/36.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: localhost:8080
Access-Control-Request-Method: GET
Access-Control-Request-Headers: x-auth-token,x-requested-with
Connection: keep-alive

Response Headers:
Access-Control-Allow-Headers: x-auth-token, x-requested-with
Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS, DELETE
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 3600
Content-Length: 0
Date: Tue, 31 Mar 2015 21:20:28 GMT
Server: Apache-Coyote/1.1

Request Method: GET
Status code:200 OK

Request Headers:
Host: localhost:9000 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:36.0) Gecko/20100101 Firefox/36.0
Accept: application/json, text/plain, /
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-Auth-Token: 80e0c2d2-dab4-435d-886e-ae28bc8e636f
X-Requested-With: XMLHttpRequest

Response Headers:
Access-Control-Allow-Headers: x-auth-token, x-requested-with
Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS, DELETE
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 3600
Content-Type: application/json;charset=UTF-8
Date: Tue, 31 Mar 2015 21:20:28 GMT
Server: Apache-Coyote/1.1
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
Transfer-Encoding: chunked
Referer: localhost:8080/
Origin: localhost:8080
Connection: keep-alive

Redis Server Keys:
1) "spring:session:expirations:1427838660000"
2) "spring:session:sessions:80e0c2d2-dab4-435d-886e-ae28bc8e636f"

Java Version:

Request Method: OPTIONS
Status code:401 Unauthorized

Request Headers:
Host: localhost:9000
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:36.0) Gecko/20100101 Firefox/36.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: localhost:8080
Access-Control-Request-Method: GET
Access-Control-Request-Headers: x-auth-token,x-requested-with
Connection: keep-alive

Response Headers:
Access-Control-Allow-Headers: x-auth-token, x-requested-with
Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS, DELETE
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 3600
Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
Content-Length: 0
Date: Tue, 31 Mar 2015 20:50:26 GMT
Server: Apache-Coyote/1.1
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
WWW-Authenticate: Basic realm="Spring"
X-Auth-Token: 8af7e1f4-e723-4ce6-8d21-54a7b10369f8

Redis Server Keys:
1) "spring:session:sessions:8af7e1f4-e723-4ce6-8d21-54a7b10369f8"
2) "spring:session:expirations:1427836860000"
3) "spring:session:sessions:c6a6cc31-eddc-40dd-99de-a6e1eecbf519"

1

1 Answers

1
votes

In Java "!=" operator is not the same as Groovy. To do string object comparison use the equals method. i.e.

if( !"OPTIONS".equals(request.getMethod()))