【问题标题】:CORS POST request fails on Chrome, Safari and FirefoxChrome、Safari 和 Firefox 上的 CORS POST 请求失败
【发布时间】:2017-08-15 19:17:12
【问题描述】:

我有一个在 localhost:8080 上运行 Spring 安全性实现的 RESTfull 后端,一个登录过滤器使用放置在标头中的令牌响应登录请求,我什至没有为此实现 Endpoint 方法,这一切都是由以下代码行的 Spring Security 魔术:

  protected void configure(HttpSecurity http) throws Exception {
        // disable caching
        http.headers().cacheControl();

        http.csrf().disable() // disable csrf for our requests.
            .authorizeRequests()
            .antMatchers("/").permitAll()
            .antMatchers("/login").permitAll()
            .anyRequest().authenticated()
            .and()
            // We filter the api/login requests
            .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class)
    …

前端是 Angularjs Nodejs NPM Bower 项目,在 localhost:8000 上运行静态 http 服务器。在前端,我发出一个简单的 POST 请求,如下所示:

   $scope.login = function () {

    var data = {
        "username":$scope.username,
        "password":$scope.password
    }

    $http({
        url: baseUrl,
        method: "POST",
        data: data
    }).then(function (response) {
        console.log("###### SUCCESS #####");
        var headers = response.headers();
        console.log("headers: "+JSON.stringify(headers));
        var token = headers.authorization.split(" ")[1];
        console.log("token: "+token);
        $http.defaults.headers.common['Authorization'] = token;
        $location.path("/view1");

    }, function (responseData) {
        // called asynchronously if an error occurs
        // or server returns responseData with an error status.
        console.log("###### FAIL #####");
        console.log("Response: "+JSON.stringify(responseData));
        $window.alert("Failed to Login");
    });

这在 IE 中很有效(也适用于 curl、wget 和 python 请求),但在 Chrome 和 Safary 上却惨遭失败。 我知道那些浏览器正在阻止 CORS POST,使请求一到达后端就为空,事实上,当我从后端注销请求时,我看不到任何数据。我尝试了所有可能的组合:

前端:

1) $http(方法:POST)

2) $http.post(

3) 添加标志:Access-Control-Allow-Origin、Access-Control-Expose等

4) 添加所有可能的标题组合:'Content-Type':'application/

浏览器端:

1) 使用标志启动 chrome:--disable-web-security

2) 安装 Chrome 扩展 CORS

后端:

1) Spring Security 禁用 csfr

2) Spring Security 全部许可

3) Spring Security HttpMethod.OPTION

没什么,NHOTING 为我工作!

我有什么遗漏吗?

还有其他发送 POST 请求的方法吗?

编辑

如前所述,我将类修改如下:

WebSecurityConfig:

        .antMatchers("/login").permitAll()
        .anyRequest().authenticated()
        .and()
        // We filter the api/login requests
        .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class)
        .addFilterBefore(new CORSFilter(), BasicAuthenticationFilter.class)

并将 CORSFilter 实现为建议集。

我还按照建议添加了 WebConfig 类:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("http://localhost:8000")
        .allowedMethods("PUT", "POST");
    }
}

由于登录过滤器抛出空字符串:

com.fasterxml.jackson.databind.JsonMappingException: 由于输入结束,没有要映射的内容

这将由拒绝访问的 Spring Security 进行聊天。

我还尝试将前端服务器移动到其他端口,然后是 8000(4200、7000 等),但没有成功。

【问题讨论】:

    标签: javascript angularjs node.js spring google-chrome


    【解决方案1】:

    您需要在 spring 中启用 Cors 支持。在您的 WebConfig 中,您可以覆盖 addCorsMappings

    @Configuration
    @EnableWebMvc
    public class WebConfig extends WebMvcConfigurerAdapter {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**").allowedOrigins("http://localhost:4200");  //url of where angular is running.
        }
    }
    

    这将为整个应用程序启用 cors。您还可以更具体地使用允许特定标头和方法的映射。

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("http://domain2.com")
                .allowedMethods("PUT", "DELETE")
                .allowedHeaders("header1", "header2", "header3");
    }
    

    您还可以在类和方法级别允许用户@CrossOrgin

    @CrossOrigin(origin = "http://domain2.com",
                 maxAge = 3600)
    public class ApiController {
    
    }
    

    http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cors.html

    https://spring.io/guides/gs/rest-service-cors/

    【讨论】:

    • 我的 webconfig 是 WebSecurityConfigurerAdapter 的子类,我必须完成更改我的应用程序结构吗?
    • 那是你对spring web security的配置。您需要创建另一个配置类并扩展 WebMvcConfigurerAdatper 然后覆盖 addCorsMappings(CorsRegistry registery) 方法。
    • 做了,还是不行!我把 WebConfig 和 SpringSecurityConfig 平行了,我需要注意 Spring 类的存在吗?
    • 取决于您如何设置应用程序。您正在使用 Spring Boot,您所需要的只是使用 @Configuration 注释这两个配置。春天会捡起来的
    • 如果它仍然不起作用,您能否添加您修改/添加的代码(配置类)以及您遇到的任何错误(如果有)。
    【解决方案2】:

    我之前用过 CORS 过滤器,效果很好:

    public class CORSFilter extends OncePerRequestFilter {
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            response.addHeader("Access-Control-Allow-Origin", "*");
    
            if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
    
                response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
                response.addHeader("Access-Control-Allow-Headers", "Content-Type");
                response.addHeader("Access-Control-Max-Age", "1");
            }
    
            filterChain.doFilter(request, response);
        }
    
    }
    

    然后将其添加到您的配置中:

    .addFilterBefore(new CORSFilter()), BasicAuthenticationFilter.class)
    

    【讨论】:

    • 谢谢,但这对我不起作用。我添加了过滤器,然后在 WebConfig 中的 loginFilter 之前添加了 addFilter 选项。它不会改变任何东西,CORS 请求仍然被阻止。
    • 您必须将其添加到此.addFilterBefore(new JWTLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class) 正下方的安全配置中,并且您必须在BasicAuthenticationFilter 之前添加它。
    猜你喜欢
    • 2021-07-20
    • 2019-06-04
    • 2018-06-09
    • 1970-01-01
    • 2012-02-05
    • 2013-06-05
    • 2018-09-25
    相关资源
    最近更新 更多