Spring Security中的CORS详解
作者:兴趣广泛的程序猿
一、CORS是什么
CORS(Cross-Origin Resource Sharing,跨源/域资源共享 )是一个W3C标准,一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本(比如AJAX)请求访问的机制,通常由于同源安全策略,浏览器会禁止这种跨域请求。
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE 浏览器不能低于 IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附件的头信息,有时还会多处一次附件的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS功能(响应报文包括了正确的CORS响应头),就可以被跨源访问(可以指定个别域或全部域)。
二、同源安全策略
说到跨域请求,就不得不说一下“同源安全策略”了,如果没有这个策略的存在,也就没有这么多事了,但是,这样可能会造成你在网站进行一些操作时存在风险。
同源策略是一个重要的安全策略,它用于限制一个源/域的文档或它加载的脚本是否能与另一个源/域的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
出于安全性,浏览器会限制脚本内发起的跨域HTTP请求,例如常见的 XHR、Fetch API 都遵循同源策略。
如果两个 URL 的协议(Protocol)、主机(Host)、端口(Port,如果有指定的话)都相同的话,那么这两个 URL 是同源的,否则是不同源的。
当浏览器中打开的某个网页,有脚本执行了跨域请求,那么,浏览器的“同源安全策略”就会介入,大致流程如下:
浏览器直接发出CORS请求,也就是在头信息之中,增加一个Origin
字段。这个字段代表本次请求来自哪个源(协议 + 主机 + 端口),服务器会根据这个值,决定是否同意这次请求。前面的流程是对于HTTP简单请求,如果是HTTP非简单请求,则会在正式请求前,增加一次预检请求。
如果Origin
指定的源,不在许可范围内(服务器端CORS功能指定),服务器会返回一个正确的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin
字段,就知道出错了,从而抛出一个错误,被XMLHttpRequest
的onerror
回调函数捕获。注意,这种错误无法通过状态识别,因为HTTP回应的状态码有可能是 200
。
抛出的错误为“has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.”,如下:
三、Spring Security中CORS的开启
在Spring框架中,我们可以在引入Spring Security依赖后,对Security的HttpSecurity进行设置,来开启CORS(跨域/源资源共享),同时能指定只被部分域/源、部分方法、部分头部信息访问资源。
Spring框架提供了CorsFilter,是为了在基于filter的安全框架(如Spring Security)上面支持CORS,或者在使用其他不支持CORS的库上支持CORS。
//(Security6.2.4写法)先创建一个普通JAVA类,如SecurityConfig.java,实现如下3个Bean。 @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests( (authz) -> authz .anyRequest().authenticated()) .httpBasic(withDefaults()) .formLogin(withDefaults()) .csrf((csrf)->csrf.disable()) .cors(withDefaults()); //开启CORS(跨域/源资源共享) return http.build(); } @Autowired //自动装配参数configurationSource(下方的Bean) @Bean public CorsFilter corsFilter(UrlBasedCorsConfigurationSource configurationSource){ return new CorsFilter(configurationSource); } // 也可以将方法内的实现整合到上面的corsFilter方法体内 @Bean public UrlBasedCorsConfigurationSource configurationSource(){ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowCredentials(true); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); corsConfiguration.addAllowedOriginPattern("http://localhost*"); //新版本写法 //corsConfiguration.addAllowedOrigin("*"); //老版本写法 source.registerCorsConfiguration("/**",corsConfiguration); return source; }
四、其它处理方法
1、Spring注解
原理:自Spring Framework 4.2开始,CORS请求(包括OPTIONS method)会被自动分发到各种注册过的HandlerMappings。它们会处理CORS的preflight请求,会拦截CORS简单和实际请求,以便基于你指定的CORS配置,添加相关的CORS响应头(如 Access-Control-Allow-Origin
)。
实现:在@RequestMapping注解过的controller method上面添加@CrossOrigin注解,表示已开启CORS。
@CrossOrigin(origins = "http://domain2.com", maxAge = 3600) //括号内为指定的CORS配置 @RestController @RequestMapping("/account") public class AccountController { @CrossOrigin("http://domain2.com") //同时使用controller级别和method级别的CORS配置,Spring会将二者的attributes结合起来,创建出融合的CORS配置。 @RequestMapping("/{id}") public Account retrieve(@PathVariable Long id) { // ... } }
除了基于注解@CrossOrigin的配置(细粒度),还可以定义全局的CORS配置。类似于使用filters,但可以定义在Spring MVC中,并与细粒度的@CrossOrigin配置相配合。
2、JSONP(JSON with Padding)
原理:利用<script>标签不受浏览器同源策略限制的特性,通过动态插入<script>标签的方式实现跨域数据访问。
实现:客户端创建一个<script>标签,将请求后端的接口URL拼接一个回调函数名称作为参数传给后端,并设置给<script>标签的src属性。后端接收到请求后,将数据和回调函数名称拼接成函数调用的形式返回。客户端在接收到响应后,会执行这个回调函数,从而获取到后端返回的数据。
3、使用代理服务器
原理:通过搭建一个代理服务器来转发客户端的请求,代理服务器与目标服务器进行通信,然后将返回的数据再转发给客户端。由于客户端和代理服务器、代理服务器和目标服务器之间的通信都在服务器端进行,因此不受浏览器同源策略的限制。
实现:可以使用Nginx、Node.js等搭建代理服务器。例如,Nginx可以通过配置反向代理来解决跨域问题;Node.js可以搭建一个中间层服务器,接收客户端的请求,然后将请求转发给目标服务器,并将返回的数据再转发给客户端。
4、前端正向代理
原理:在客户端设置一个代理服务器,并指定目标服务器。代理服务器向目标服务器转交请求,并将获得的内容发送给客户端。这种方式在客户端进行了请求转发,但同样实现了跨域请求。
实现:可以通过在客户端代码中配置代理服务器地址和端口,以及目标服务器的地址和端口来实现。在发送请求时,将请求地址替换为代理服务器的地址,并在代理服务器中进行请求转发。
5、配置浏览器插件或工具
有些浏览器插件或工具(如Postman、curl等)可以绕过浏览器的同源策略限制,直接发送跨域请求。但这种方法主要用于开发和测试阶段,不建议在生产环境中使用。
补充:关于Spring Security的CSRF
关于Spring Security的CSRF
一、CSRF是什么
CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种常见的网络攻击方式,攻击者通过伪装用户的请求,利用用户在其他网站上已经登录的身份权限来执行恶意操作。为了防止 CSRF 攻击,Spring Security 提供了内置的 CSRF 保护功能。
二、Spring Security中的CSRF保护
在Spring框架中,如果引入了Spring Security依赖,除了会默认开启身份验证,同时还会开启CSRF保护功能。
启用此功能可确保,在会更改状态的HTTP请求上,如果没有带上有效的X-CSRFToken,则会出现 403 错误。
HTTP状态码403表示禁止访问,即服务器理解请求客户端的请求,但是拒绝执行这个请求。这通常是由于客户端没有足够的权限访问该资源所导致的。
可以认为安全的方法都是只读的方法(GET, HEAD, OPTIONS),这些类型的请求不会改变资源状态,被认为是幂等的,即调用相同的URL多次得到的结果不变。
DELETE方法的语义表示删除服务器上的一个资源,第一次删除成功后该资源就不存在了,资源状态改变了,所以DELETE方法不具备安全特性。然而HTTP协议规定DELETE方法是幂等的,每次删除该资源都要返回状态码200 OK,服务器端要实现幂等的DELETE方法,必须记录所有已删除资源的元数据(Metadata),否则,第二次删除后返回的响应码就会类似404 Not Found了。
PUT和POST方法语义中都有修改资源状态的意思,因此都不是安全的。但是PUT方法是幂等的,POST方法不是幂等的,这么设计的理由是:
HTTP协议规定,POST方法修改资源状态时,URL指示的是该资源的父级资源,待修改资源的ID信息在请求体中携带。而PUT方法修改资源状态时,URL直接指示待修改资源。因此,同样是创建资源,重复提交POST请求可能产生两个不同的资源,而重复提交PUT请求只会对其URL中指定的资源起作用,也就是只会创建一个资源。
因此,当Spring Security使用默认配置,或手动开启CSRF保护功能时,我们会发现,GET请求是正常的,但是POST请求会报403错误。
三、处理方法
处理方法一:跳过安全检查(不推荐,因为这个一般是用于静态资源等)
@Bean //(Spring Security6.2.4写法) public WebSecurityCustomizer webSecurityCustomizer() { // 匹配的路径,直接跳过安全检查 return (web) -> web.ignoring().requestMatchers("/static/**"); }
处理方法二:关闭CSRF保护功能
@Bean //(Spring Security6.2.4写法) public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests( (authz) -> authz .anyRequest().authenticated()) .httpBasic(withDefaults()) .formLogin(withDefaults()) .csrf((csrf)->csrf.disable()); //只对匹配路径关闭CSRF //.csrf((csrf)->csrf.ignoringRequestMatchers("/路径")); return http.build(); }
处理方法三:正常在请求时带上X-CSRFToken
到此这篇关于关于Spring Security的CORS的文章就介绍到这了,更多相关Spring Security的CORS内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!