首页 文章

Spring安全CORS过滤器

提问于
浏览
13

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

从此刻开始,我们的服务器出现了401 No 'Access-Control-Allow-Origin' header is present on the requested resource 错误 .

那是因为没有 Access-Control-Allow-Origin 标头附加到响应 . 为了解决这个问题,我们在注销过滤器之前添加了我们自己的过滤器,它位于 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();
}
}

我们的过滤器是从spring-boot注册的:

2016-11-04 09:19:51.494 INFO 9704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean:映射过滤器:'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 .Excepti onTranslationFilter @ 4b8bf1fb,org.springframework.security.web.access.intercept.FilterSecurityInterceptor@42063cf1]

回复:Response headers

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

编辑1:

尝试了@PiotrSołtysiak的解决方案 . cors过滤器未在生成的过滤器链中列出,我们仍然会得到相同的错误 .

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.SecurityContextHolderAw areRequestFilter @ 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版本 .

8 回答

  • 38

    从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

  • 0

    好的,经过2天的搜索,我们终于解决了问题 . 我们删除了所有过滤器和配置,而是在应用程序类中使用了这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");
            }
        };
    }
    }
    
  • 6
    • 您不需要:
    @Configuration
    @ComponentScan("com.company.praktikant")
    

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

    • 关于CORS过滤器,我只想把它:
    @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;
    }
    

    进入SecurityConfigurationclass和remove配置和配置全局方法 . 您不需要两次设置allowde orgins,headers和方法 . 特别是如果你在过滤器和 spring 安全配置中添加不同的属性:)

    • 根据上述内容,您的“MyFilter”类是多余的 .

    • 你也可以删除那些:

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

    来自Application类 .

  • 2

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

    @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
        }
    
    }
    
  • 7

    就我而言,我刚刚添加了这个类并使用了@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");
    }
    

    }

  • 2

    根据CORS filter documentation

    “Spring MVC通过控制器上的注释为CORS配置提供了细粒度的支持 . 但是当与Spring Security一起使用时,建议依赖必须在Spring Security的过滤器链之前订购的内置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回答 .

  • 9

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

    @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;
    }
    

    但在我的情况下,它会抛出一个意外的类类型异常 . 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);
    }
    
  • 2

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

相关问题