Spring安全CORSfilter

我们在现有项目中添加了Spring Security

从这一刻起,我们得到一个401 No 'Access-Control-Allow-Origin' header is present on the requested resource我们服务器No 'Access-Control-Allow-Origin' header is present on the requested resource错误上。

那是因为没有Access-Control-Allow-Origin标头附加到响应。 为了解决这个问题,我们在注销过滤Filter之前添加了我们自己的filter,它在Filter链中,但filter不适用于我们的请求。

我们的错误:

XMLHttpRequest无法加载http://localhost:8080/getKunden 。 请求的资源上不存在“Access-Control-Allow-Origin”标头。 原因http://localhost:3000因此不允许访问。 响应具有HTTP状态代码401。

我们的安全配置:

 @EnableWebSecurity @Configuration @ComponentScan("com.company.praktikant") public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private MyFilter filter; @Override public void configure(HttpSecurity http) throws Exception { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); final CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); config.addAllowedMethod("GET"); config.addAllowedMethod("PUT"); config.addAllowedMethod("POST"); source.registerCorsConfiguration("/**", config); http.addFilterBefore(new MyFilter(), LogoutFilter.class).authorizeRequests() .antMatchers(HttpMethod.OPTIONS, "/*").permitAll(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { } } 

我们的滤镜

 @Component public class MyFilter extends OncePerRequestFilter { @Override public void destroy() { } private String getAllowedDomainsRegex() { return "individual / customized Regex"; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final String origin = "http://localhost:3000"; response.addHeader("Access-Control-Allow-Origin", origin); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Headers", "content-type, x-gwt-module-base, x-gwt-permutation, clientid, longpush"); filterChain.doFilter(request, response); } } 

我们的应用

 @SpringBootApplication public class Application { public static void main(String[] args) { final ApplicationContext ctx = SpringApplication.run(Application.class, args); final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(CORSConfig.class); annotationConfigApplicationContext.refresh(); } } 

我们的filter是从spring-boot注册的:

2016-11-04 09:19:51.494 INFO 9704 — [ost-startStop-1] osbwservlet.FilterRegistrationBean:映射filter:’myFilter’为:[/ *]

我们生成的过滤链:

2016-11-04 09:19:52.729 INFO 9704 — [ost-startStop-1] ossweb.DefaultSecurityFilterChain:创建过滤链:org.springframework.security.web.util.matcher.AnyRequestMatcher@1,[org.springframework .security.web.context.request.async.WebAsyncManagerIntegrationFilter @ 5d8c5a8a,org.springframework.security.web.context.SecurityContextPersistenceFilter @ 7d6938f,org.springframework.security.web.header.HeaderWriterFilter @ 72aa89c,org.springframework.security.web .csrf.CsrfFilter @ 4af4df11,com.company.praktikant.MyFilter @ 5ba65db2,org.springframework.security.web.authentication.logout.LogoutFilter @ 2330834f,org.springframework.security.web.savedrequest.RequestCacheAwareFilter@396532d1,org.springframework .security.web.servletapi.SecurityContextHolderAwareRequestFilter @ 4fc0f1a2,org.springframework.security.web.authentication.AnonymousAuthenticationFilter@2357120f,org.springframework.security.web.session.SessionManagementFilter @ 10867bfb,org.springframework.security.web.access.ExceptionTra nslationFilter @ 4b8bf1fb,org.springframework.security.web.access.intercept.FilterSecurityInterceptor@42063cf1]

响应: 响应标头

我们从spring尝试了解决方案,但它没有用! 我们控制器中的注释@CrossOrigin也没有帮助。

编辑1:

试过@PiotrSołtysiak的解决方案。 corsfilter未在生成的filter链中列出,我们仍然会得到相同的错误。

2016-11-04 10:22:49.881 INFO 8820 — [ost-startStop-1] ossweb.DefaultSecurityFilterChain:创建过滤链:org.springframework.security.web.util.matcher.AnyRequestMatcher@1,[org.springframework .security.web.context.request.async.WebAsyncManagerIntegrationFilter @ 4c191377,org.springframework.security.web.context.SecurityContextPersistenceFilter @ 28bad32a,org.springframework.security.web.header.HeaderWriterFilter @ 3c3ec668,org.springframework.security.web .csrf.CsrfFilter @ 288460dd,org.springframework.security.web.authentication.logout.LogoutFilter @ 1c9cd096,org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@3990c331,org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter @ 1e8d4ac1,org.springframework.security.web.authentication.www.BasicAuthenticationFilter@2d61d2a4,org.springframework.security.web.savedrequest.RequestCacheAwareFilter@380d9a9b,org.springframework.security.web.servletapi.SecurityContextHolderAwareRe questFilter @ abf2de3,org.springframework.security.web.authentication.AnonymousAuthenticationFilter@2a5c161b,org.springframework.security.web.session.SessionManagementFilter@3c1fd3e5,org.springframework.security.web.access.ExceptionTranslationFilter @ 3d7055ef,org.springframework。 security.web.access.intercept.FilterSecurityInterceptor@5d27725a]

顺便说一下,我们使用的是spring-security 4.1.3版本。

从Spring Security 4.1开始,这是使Spring Security支持CORS的正确方法(在Spring Boot 1.4 / 1.5中也需要):

 @Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH"); } } 

和:

 @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // http.csrf().disable(); http.cors(); } @Bean public CorsConfigurationSource corsConfigurationSource() { final CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(ImmutableList.of("*")); configuration.setAllowedMethods(ImmutableList.of("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH")); // setAllowCredentials(true) is important, otherwise: // The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. configuration.setAllowCredentials(true); // setAllowedHeaders is important! Without it, OPTIONS preflight request // will fail with 403 Invalid CORS request configuration.setAllowedHeaders(ImmutableList.of("Authorization", "Cache-Control", "Content-Type")); final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } } 

不要执行以下任何操作,这是尝试解决问题的错误方法:

  • http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
  • web.ignoring().antMatchers(HttpMethod.OPTIONS);

参考: http : //docs.spring.io/spring-security/site/docs/4.2.x/reference/html/cors.html

好的,经过2天的搜索,我们终于解决了问题。 我们删除了所有的filter和配置,而是在应用程序类中使用了这5行代码。

 @SpringBootApplication public class Application { public static void main(String[] args) { final ApplicationContext ctx = SpringApplication.run(Application.class, args); } @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedOrigins("http://localhost:3000"); } }; } } 
  1. 你不需要:

     @Configuration @ComponentScan("com.company.praktikant") 

    @EnableWebSecurity中已经有@Configuration,我无法想象为什么你把@ComponentScan放在那里。

  2. 关于CORSfilter,我只想说:

     @Bean public FilterRegistrationBean corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); source.registerCorsConfiguration("/**", config); FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); bean.setOrder(0); return bean; } 

    进入SecurityConfiguration类并删除co​​nfigure和configure全局方法。 您不需要两次设置allowde orgins,headers和方法。 特别是如果你在filter和弹簧安全配置中添加不同的属性:)

  3. 根据以上所述,您的“MyFilter”类是多余的。

  4. 你也可以删除那些:

     final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(CORSConfig.class); annotationConfigApplicationContext.refresh(); 

    来自Application类。

  5. 最后小建议 – 与问题无关。 你不想把动词放在URI中。 您应该在http://localhost:8080/kunden资源上使用HTTP GET方法而不是http://localhost:8080/kunden 。 您可以在此处了解设计RESTful api的最佳实践: http : //www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

由于我遇到其他解决方案的问题(特别是为了让它在所有浏览器中运行,例如edge不能将“*”识别为“Access-Control-Allow-Methods”的有效值),我必须使用自定义filter组件,它最终为我工作,并完成我想要实现的目标。

 @Component @Order(Ordered.HIGHEST_PRECEDENCE) public class CorsFilter implements Filter { 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-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "ACL, CANCELUPLOAD, CHECKIN, CHECKOUT, COPY, DELETE, GET, HEAD, LOCK, MKCALENDAR, MKCOL, MOVE, OPTIONS, POST, PROPFIND, PROPPATCH, PUT, REPORT, SEARCH, UNCHECKOUT, UNLOCK, UPDATE, VERSION-CONTROL"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Key, Authorization"); if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); } else { chain.doFilter(req, res); } } public void init(FilterConfig filterConfig) { // not needed } public void destroy() { //not needed } } 

就我而言,我刚刚添加了这个类并使用了@EnableAutConfiguration

 package com.package.filter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.filter.GenericFilterBean; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class SimpleCORSFilter extends GenericFilterBean { /** * The Logger for this class. */ private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { logger.info("> doFilter"); HttpServletResponse response = (HttpServletResponse) resp; 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", "Authorization, Content-Type"); //response.setHeader("Access-Control-Allow-Credentials", "true"); chain.doFilter(req, resp); logger.info("< doFilter"); } 

}

在很多地方,我看到需要添加此代码的答案:

 @Bean public FilterRegistrationBean corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); source.registerCorsConfiguration("/**", config); FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); bean.setOrder(0); return bean; } 

但在我的情况下,它会抛出一个意外的类类型exception。 corsFilter() bean需要CorsFilter类型,所以我做了这个更改并将bean的这个定义放在我的配置中,现在一切正常。

 @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } 

根据CORSfilter文档 :

“Spring MVC通过控制器上的注释为CORS配置提供了细粒度的支持。但是当与Spring Security一起使用时,建议依赖必须在Spring Security的filter链之前订购的内置CorsFilter

像这样的东西将允许GET访问/ajaxUri

 import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import java.util.Arrays; @Component @Order(Ordered.HIGHEST_PRECEDENCE) public class AjaxCorsFilter extends CorsFilter { public AjaxCorsFilter() { super(configurationSource()); } private static UrlBasedCorsConfigurationSource configurationSource() { CorsConfiguration config = new CorsConfiguration(); // origins config.addAllowedOrigin("*"); // when using ajax: withCredentials: true, we require exact origin match config.setAllowCredentials(true); // headers config.addAllowedHeader("x-requested-with"); // methods config.addAllowedMethod(HttpMethod.OPTIONS); config.addAllowedMethod(HttpMethod.GET); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/startAsyncAuthorize", config); source.registerCorsConfiguration("/ajaxUri", config); return source; } } 

当然,您的SpringSecurity配置必须允许使用列出的方法访问URI。 见@Hendy Irawan回答。

对于已部署且无法承受代码更改的程序(例如添加/更新弹簧安全性),添加简单代理是一种解决方案: https : //stackoverflow.com/a/49827300/1758194