转载自网络,如有侵权请联系删除
背景
前后端分离的模式,前端应用通过跨域的方式直连后端机器。
跨域请求的类型
简单请求
request method 属于
GET
,HEAD
,POST
三种简单类型之一。headers 只包含以下几种 (大小写不敏感):
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
非简单请求 (preflight)
- 不符合以上简单请求规则的, 浏览器在发送请求前,会先向服务器发送一个 OPTIONS 请求, (称为 preflight), 只有当 preflight 收到 200/204 的回复时,才会发送真实的请求。
响应非简单请求
有些后端应用可能只有 GET 和 POST 两种请求方式,是否还需要能响应 preflight 请求呢?
答案是需要的,比如你的前端应用在多环境部署,可能需要在请求头中添加 X-Antcloud-Tenant
header,或者是某些请求库,默认会添加 x-request-lib
这一请求头。因此,原来符合简单规则的请求,也有极大的可能会变成 preflight 请求。所以后端需要做的是,在收到 OPTIONS
请求时,返回 200/204 的状态码。
跨域相关的 headers
Request Headers:
Access-Control-Request-Headers
- 浏览器自动添加,在 preflight 请求中,浏览器会加上真实请求中出现的 headers,后端可以根据该字段决定 Access-Control-Allow-Headers 的值。
Response Headers:
Access-Control-Allow-Origin
- 允许跨域的的域名,不推荐使用通配符(*), 因为在使用通配符时,会跳过 Access-Control-Allow-Credentials 的配置,直接过滤掉请求携带的 cookie. 可能导致后端接口鉴权失败。
Access-Control-Allow-Credentials
- 是否允许跨域请求携带 cookie,通常情况下,用户信息和登陆状态都储存在 cookie 中。如果不携带 cookie 则无法通过后端的接口鉴权,需要设置为 true.
Access-Control-Allow-Headers
- 允许出现的非简单请求头。
Access-Control-Allow-Methods
- 允许跨域的方法,如果没有特殊的安全考虑,可以针对 POST,GET,OPTIONS,DELETE,PUT,HEAD,PATCH 请求都放开,让所有符合 restful 规范的请求都能跨域。
Access-Control-Expose-Headers
- 非必填,如果前端需要消费非简单请求。则需通过该配置指定额外允许前端获得的 headers。部分情况下,浏览器的返回请求中出现 provisonal header,也与此配置有关。
Access-Control-Max-Age
- 非必填,preflight 请求的有效期。
通过 nginx 配置跨域
通过上面请求头字段的解释,我们可以通过 nginx 允许后端应用跨域。在后端项目的 conf/auto-conf/t-alipay-engine.conf 文件中,可以对后端路径增加如下配置:
1 | location ^~ /api { |
需要注意的是,目前 nginx 1.7.5 之前的版本,add_header 只对 2xx,3xx 的请求生效,5xx 的请求无法增加 header,仍会被浏览器跨域策略拦截,在 5xx 请求的 body 中包含的错误信息,前端无法获取到。
因此,通过这种策略配置的跨域,后端需要将错误信息(例如:参数错误、未登陆)包含在一个 200 的请求内返回。而不能通过 401、502 等 http code 返回,否则前端将无法获取接口出错时的内容。
通过 Java Filter 配置跨域
以下内容摘自 SOFA 官网,原链接
MVC 4.2.0 之前框架默认支持的方法是GET、HEAD、POST:相关代码,如果跨域方法不在这些方法列表中,可能报错,且MVC 4.2.0之前版本不支持修改CORS跨域相关配置,如果报错建议升级到4.2.2以上版本。
MVC 4.2.2 配置的Filter默认只会允许阿里域的域名访问,如果需要允许其他域名访问,可以在MERGE-INF/merge-smvc-scheme-default.xml文件中配置以下bean覆盖框架的默认bean:
1 | <bean id="defaultCorsFilter" class="com.alipay.sofa.web.mvc.security.filter.DefaultCorsFilter"> |
不同 Java 技术栈通过 filter 配置的方式可能略有不同,以 HUANYU 为例,sofaboot 3.1.0:
1 | <parent> |
1)引入依赖的 JAR
1 | <dependency> |
2)在 merge-smvc-scheme-default.xml 文件中添加
1 | <bean id="defaultCorsFilter" class="com.alipay.common.security.filter.DefaultCorsFilter"> |
手动配置跨域
如果上面的方法对你的技术栈不适用,同时 nginx 只能修改 200 请求的方式不能满足业务的需求,也可以通过 HttpServletResponse 手动添加上述跨域的 headers,在最终组装 http 返回结果前(如 Interceptor 中)调用,例如:
1 | import javax.servlet.http.HttpServletRequest; |
FAQ
- 如果明知会跨域,有没有办法禁用本地的安全策略,方便开发和调试?
1 | open -n -a /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security |
可以通过以上命令启动一个不安全的 chrome(Mac OS), 在本地开发时非常有用,此模式下所有请求都不会被跨域策略拦截,且不会出现 provisional header.
- 跨域请求是否可以发起 redirect?
浏览器允许跨域的简单请求进行重定向,但是考虑到浏览器兼容性,不推荐进行多次的重定向。对于 preflight 请求,redirect 的地址不会被 follow.