为啥 Spring Security 身份验证会导致 CORS 错误

Posted

技术标签:

【中文标题】为啥 Spring Security 身份验证会导致 CORS 错误【英文标题】:Why Spring Security authentication causes CORS error为什么 Spring Security 身份验证会导致 CORS 错误 【发布时间】:2019-06-24 08:24:21 【问题描述】:

我有一个用 Java 制作的带有 Spring Boot、Security 和 Web 的后端服务器和一个用 Angular 制作的客户端。

目前我正在尝试在localhost:8080/resource 下提出一个简单的请求。

该地址的控制器如下所示:

@RestController
public class IndexController 
    @CrossOrigin
    @RequestMapping("/resource")
    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;
    

Angular 客户端(执行请求的部分)是这样的:

import  Component  from "@angular/core";
import  HttpClient  from "@angular/common/http";

@Component(
    selector: "app-root",
    templateUrl: "./app.component.html",
    styleUrls: ["./app.component.css"]
)
export class AppComponent 
    public title = "Security Client";
    public greeting = ;

    constructor(private http: HttpClient) 
        http.get("http://localhost:8080/resource").subscribe(data => this.greeting = data);
    

仅使用显示的内容的问题是我收到了 CORS 错误。

是否从我的pom.xml 中删除 Spring Security 或添加此配置:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 

    @Override
    protected void configure(HttpSecurity http) throws Exception 

        http.authorizeRequests().antMatchers("/resource").permitAll();
    

解决问题。

我想知道的是为什么我在访问需要用户身份验证的地址时收到 CORS 错误而不是 401 Unauthorized。

【问题讨论】:

【参考方案1】:

根据 spring boot 文档:

出于安全原因,浏览器禁止 AJAX 调用资源 在当前原点之外。例如,您可以让您的银行 帐户在一个选项卡中,而 evil.com 在另一个选项卡中。来自 evil.com 的脚本 应该无法使用您的银行 API 向您的银行 API 发出 AJAX 请求 凭据 — 例如从您的帐户中提款!

跨域资源共享 (CORS) 是 W3C 规范 由大多数浏览器实现,可让您指定哪种类型的 跨域请求是授权的,而不是使用不太安全的 以及基于 IFRAME 或 JSONP 的不太强大的解决方法。

您收到此错误是因为您需要在安全配置中添加过滤器。在您的配置中,添加:

@Override
protected void configure(HttpSecurity http) throws Exception 
    http.cors()
    .and()
    .authorizeRequests().antMatchers("/resource").permitAll();

在同一个文件中,你应该添加:

@Bean
public CorsConfigurationSource corsConfigurationSource() 
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("*"));
    configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", 
    "DELETE", "OPTIONS"));
    configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", 
    "x-auth-token"));
    configuration.setExposedHeaders(Arrays.asList("x-auth-token"));
    UrlBasedCorsConfigurationSource source = new 
    UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);

    return source;

这对我来说很好。

【讨论】:

但是为什么没有它我得到 CORS 错误而不是 401? 我认为这是因为 cors 过滤器是在身份验证之前处理的。 CORS 验证发生在通过 OPTIONS 请求(通过飞行前请求调用)发出 POST、GET... 请求之前。要更好地了解请求流程,您可以查看 this 和 this【参考方案2】:

我想知道的是为什么我会收到 CORS 错误而不是 401 访问需要用户的地址时未经授权 身份验证。

您收到此错误是因为在您的实际请求(POST、GET...)之前,浏览器会执行飞行前请求(OPTIONS)以验证被调用服务器是否能够处理 CORS 请求。

在此请求期间,Access-Control-Request-MethodAccess-Control-Request-Header 被验证,并且一些其他信息被添加到标头中。

然后您会收到 CORS 错误,因为如果 CORS 验证对 OPTIONS 请求失败您的实际请求甚至没有完成

您可以查看 CORS 验证如何工作的流程图in here

有趣的一点是,当服务器无权回答 OPTIONS 请求时,您只会在飞行前请求期间收到类似 401 的 HTTP 错误状态。

【讨论】:

以上是关于为啥 Spring Security 身份验证会导致 CORS 错误的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Spring boot Security Basic Authentication 慢?

Spring-Security:优化获取当前经过身份验证的用户...

Spring Security 中的基本身份验证(身份验证失败消息)

为啥 Spring Security getAuthentication() 在服务器上返回 null

Spring MVC REST + Spring Security + 基本身份验证

不使用 Spring Security 身份验证?