Spring Boot安全性忽略cors配置

3
我已经将Spring Security(5.0.0.RELEASE)与JWT身份验证添加到了Spring Boot(1.5.7.RELEASE),但CORS似乎不起作用。我按照此处描述的添加了CORS配置。我也尝试将@CrossOrigin添加到控制器中,但似乎没有改变什么。
当从前端(Angular JS 5)调用POST和GET请求时,似乎没有CORS问题,但是在调用DELETE时,我会收到CORS错误。
以下是安全配置内容:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurity extends WebSecurityConfigurerAdapter {
    private UserDetailsService userDetailsService;
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    public WebSecurity(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.userDetailsService = userDetailsService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                .cors()
                .and()
                .csrf()
                .disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
                .antMatchers(HttpMethod.GET, ACTIVATE_URL).permitAll()
                .antMatchers(AUTH_WHITELIST).permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilter(new JWTAuthenticationFilter(authenticationManager()))
                .addFilter(new JWTAuthorizationFilter(authenticationManager()))
                // this disables session creation on Spring Security
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration configuration = new CorsConfiguration().applyPermitDefaultValues();
        configuration.setAllowedOrigins(Collections.singletonList("*"));
        configuration.addAllowedMethod(HttpMethod.TRACE);

        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

这里是用Postman发送OPTIONS请求时的标头:

Allow →DELETE,GET,HEAD,POST
Cache-Control →no-cache, no-store, max-age=0, must-revalidate
Content-Length →0
Date →Fri, 12 Jan 2018 13:22:08 GMT
Expires →0
Pragma →no-cache
X-Content-Type-Options →nosniff
X-Frame-Options →DENY
X-XSS-Protection →1; mode=block

缺少允许访问控制源头的头信息,以及我添加的HTTP TRACE来查看配置是否有效。

以及Spring安全性调试日志:

2018-01-12 14:22:08.621 DEBUG 15619 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /api/stories/7 at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2018-01-12 14:22:08.621 DEBUG 15619 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /api/stories/7 at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2018-01-12 14:22:08.621 DEBUG 15619 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /api/stories/7 at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2018-01-12 14:22:08.621 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@7ce27d90
2018-01-12 14:22:08.621 DEBUG 15619 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /api/stories/7 at position 4 of 13 in additional filter chain; firing Filter: 'CorsFilter'
2018-01-12 14:22:08.621 DEBUG 15619 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /api/stories/7 at position 5 of 13 in additional filter chain; firing Filter: 'LogoutFilter'
2018-01-12 14:22:08.621 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', GET]
2018-01-12 14:22:08.621 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'OPTIONS /api/stories/7' doesn't match 'GET /logout
2018-01-12 14:22:08.621 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', POST]
2018-01-12 14:22:08.621 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'OPTIONS /api/stories/7' doesn't match 'POST /logout
2018-01-12 14:22:08.621 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', PUT]
2018-01-12 14:22:08.621 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'OPTIONS /api/stories/7' doesn't match 'PUT /logout
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', DELETE]
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'OPTIONS /api/stories/7' doesn't match 'DELETE /logout
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /api/stories/7 at position 6 of 13 in additional filter chain; firing Filter: 'JWTAuthenticationFilter'
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/stories/7'; against '/api/users/login'
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /api/stories/7 at position 7 of 13 in additional filter chain; firing Filter: 'JWTAuthorizationFilter'
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /api/stories/7 at position 8 of 13 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /api/stories/7 at position 9 of 13 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /api/stories/7 at position 10 of 13 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.a.AnonymousAuthenticationFilter  : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /api/stories/7 at position 11 of 13 in additional filter chain; firing Filter: 'SessionManagementFilter'
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /api/stories/7 at position 12 of 13 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /api/stories/7 at position 13 of 13 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'OPTIONS /api/stories/7' doesn't match 'POST /api/users/register
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'OPTIONS /api/stories/7' doesn't match 'GET /api/users/activate/**
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/stories/7'; against '/api/users/login'
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/stories/7'; against '/api/users/request-reset-password'
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/stories/7'; against '/api/users/reset-password'
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/stories/7'; against '/api/stories'
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/stories/7'; against '/api/stories/*'
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /api/stories/7; Attributes: [permitAll]
2018-01-12 14:22:08.622 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2018-01-12 14:22:08.623 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@703f0616, returned: 1
2018-01-12 14:22:08.623 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.a.i.FilterSecurityInterceptor    : Authorization successful
2018-01-12 14:22:08.623 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.a.i.FilterSecurityInterceptor    : RunAsManager did not change Authentication object
2018-01-12 14:22:08.623 DEBUG 15619 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /api/stories/7 reached end of additional filter chain; proceeding with original chain
2018-01-12 14:22:08.630 DEBUG 15619 --- [nio-8080-exec-3] o.s.s.w.a.ExceptionTranslationFilter     : Chain processed normally
2018-01-12 14:22:08.630 DEBUG 15619 --- [nio-8080-exec-3] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

这里是无效CORS请求的日志:

2018-01-12 15:47:09.445 DEBUG 17909 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /api/users/bank-accounts/ at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2018-01-12 15:47:09.445 DEBUG 17909 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /api/users/bank-accounts/ at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2018-01-12 15:47:09.445 DEBUG 17909 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /api/users/bank-accounts/ at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2018-01-12 15:47:09.445 DEBUG 17909 --- [nio-8080-exec-2] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@7d9c9d3c
2018-01-12 15:47:09.446 DEBUG 17909 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : /api/users/bank-accounts/ at position 4 of 13 in additional filter chain; firing Filter: 'CorsFilter'
2018-01-12 15:47:09.446 DEBUG 17909 --- [nio-8080-exec-2] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

如果我没有指定origin头,则会收到来自该问题的响应,当我在origin头中放入http://localhost:4200/时,会收到无效的CORS请求。 - Coka
只有当请求包含“origin”标头时,CORS才能正常工作。您的浏览器会自动添加此标头,但我不知道Postman是否也会这样做。 - dur
这是因为 allowMethodsnull,所以将 configuration.setAllowedMethods(Arrays.asList("GET", "POST", "DELETE", "OPTIONS")); 添加到配置中使请求有效。不确定为什么 CorsConfiguration#applyPermitDefaultValues 中的 allowMethods 为空。 - Coka
你的问题解决了吗? - dur
是的,谢谢。 - Coka
1个回答

1
当使用 dur 建议的断点时,结果显示 allowMethods 总是为 null
将以下行添加到 corsConfigurationSource 中即可解决问题:
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "DELETE", "OPTIONS"));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接