Spring Boot教程(14) – 跨域访问

前两天,看了看前端小伙伴的代码,发现他在访问API的时候,并不是直接访问API域名的,而是访问前端域名下的/api路径,然后在部署的服务器上,用nginx把/api路径的请求,转发到API域名那里。我感觉很奇怪,为什么要这样做?本来可以直接访问的东西,为啥还要再绕一圈?

后来搜了搜,发现了同源策略(SOP,Same-origin policy)这种东西。默认情况下,一个网站只能访问自己的域名下的接口,准确的说,只有协议(http,https),域名,端口全都一致的情况下,才可以访问。正是因为这个原因,前端小伙伴才选择将API请求通过nginx转发到真实的API服务器。

我们知道浏览器是个公共场所,用户会访问很多网站。假如没有同源策略,A网站就可以访问B网站的接口或者网页,因为访问B网站的时候会带上B的Cookie信息,所以可以获取到几乎所有的用户数据,这时B网站就毫无安全可言了,所以需要用同源策略来隔离。同源策略只适用于JS代码,你在网页的HTML里还是可以通过img,script等标签获取静态资源的(比如你放在CDN上的js或图片),或者通过form标签提交表单。

我还想跨域怎么办?跨域资源共享(CORS) 是这样一种可以实现跨域的机制,大部分浏览器都支持。API服务器通过返回特别的HTTP Header,来告诉浏览器,谁可以访问这个服务器。

原来跨域是通过服务器进行控制的😱,也就是说,你只有请求了之后,才能知道是否被允许。CORS将请求分为两种,一种是简单请求,一种是非简单请求,他俩的区别你可以通过这里查看。对于非简单请求,浏览器会用HTTP OPTIONS方法先发出一个预检请求(preflight request),服务器同意了之后,再发出实际的请求。而简单请求则没有这一过程。

服务器返回的Header大致有以下几种:

  • Access-Control-Allow-Origin 值写成http://example.com这种形式指明谁可以访问,或者传*表示谁都可以
  • Access-Control-Expose-Headers 哪些Header能在响应中列出,默认只有6个Header可以。服务器其实可以返回很多Header,但是浏览器暴露给js的Header是经过筛选的
  • Access-Control-Allow-Credentials 有点复杂,我还不太懂😂
  • Access-Control-Max-Age 预检请求的结果的缓存时间,单位为秒
  • Access-Control-Allow-Methods 预检请求中指明允许的HTTP方法
  • Access-Control-Allow-Headers 预检请求中指明允许的Header

如果你仔细研究协议的内容,会晕掉的,别管那么多,毕竟大多数情况下,我们只关心Access-Control-Allow-Origin,而且没必要控制的那么细。接下来看看Spring Boot中是怎么支持CORS的。

你可以创建一个WebMvcConfigurer类型的Bean,或者让@Configuration配置类实现WebMvcConfigurer接口,来配置全局的CORS支持:

默认情况下,上图的配置,会允许所有的网站访问,允许所有的header,允许GET、POST和HEAD请求,maxAge设置为30分钟。当然你可以自定义很多参数:

通常我会把allowedOrigins设置为前端服务器的域名(包括http和https),会把allowedMethods设置为允许所有方法。简单易懂好控制。

Spring还支持使用@CrossOrigin注解来更精细地控制跨域的粒度,比如加在@Controller类上,控制对应路径的跨域设置,更可以添加到具体的控制器方法上,如果你有需要,可以研究研究。Spring还包含了CorsFilter,用来实现跟全局配置CORS一样的效果,Spring Security也使用了CorsFilter完成跨域的需求,使用时请注意。

CORS的作用范围也只是浏览器,如果你用curl或者postman或者普通程序都是可以访问的。所以我在想,到底CORS有没有意义,毕竟别人知道了你的API之后,随便写个程序都可以调用了,还用在乎你CORS不CORS?如果是在浏览器端,你的用户token存在LocalStorage里的,就算别的网站可以访问你的API,也获取不到你的LocalStorage里的东西呀,也是安全的。后来我想了想,别人可以写个网站调用你的接口来提供服务,这样流量就是别人的了。所以allowedOrigins还是设置一下比较好。

我在后端代码里配置完CORS以后,前端小伙伴就不用再做转发的工作了。项目的工作逻辑清晰度+1,开发和部署环境的设置复杂度-1。你或者你的后端小伙伴看了本文还是不懂?那就看看Spring的文档吧:Spring-MVC-CORS

发表评论

电子邮件地址不会被公开。