前端调用不同域的后端接口,满足三个条件即产生跨域问题:
- 浏览器限制(Access-Control-Allow-Origin)
- 跨域(协议/域名/端口不同)
- XHR(XMLHttpRequest)请求
一些要点:
-
<img>
(用于打点统计),<script>
(JSONP,见《前后端交互的方式》),<link>
默认允许跨域,防盗链除外。 - 所有跨域请求都必须经过信息提供方允许;
- 浏览器存在漏洞时可以未经允许即获取(服务端不做限制);
解决思路
- 解除浏览器限制(disable-web-security)
- JSONP(发送类型为script,返回js函数调用、参数为Json对象的脚本,需要后端代码callback支持)
@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice{
public JsonpAdvice() {
super("callback");
}
}
// 存在问题:服务器需要改动代码、只支持GET方法、发送的请求非XHR请求。
- 被调用方:支持跨域,请求从浏览器发送到被调用方,服务端在响应头增加字段告知浏览器允许跨域访问
filter
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Headers
- 调用方:隐藏跨域,修改调用方服务端、转发请求
简单请求
- 简单请求:GET、HEAD、POST方法,且没有自定义头,Content-Type为text/plain、multipart/form-data、application/x-www-form-urlencoded;
- 非简单请求:PUT、DELETE方法,JSON,带自定义头。
非简单请求都会先发送OPTIONS预检命令(检查是否跨域),可以被缓存减少每次请求的开销。
带Cookie的跨域请求
带(被调用方的)Cookie时Access-Control-Allow-Origin必须与当前域完全匹配,不能使用*(服务端可在请求头的Origin中获取、设置到响应头Access-Control-Allow-Origi中),且带上Access-Control-Allow-Credentials=true。
带自定义头的跨域请求
从请求头获取Access-Control-Request-Headers,设置到响应头Access-Control-Allow-Headers中。
解决方法总结
从被调用方解决
- NginX、Apache配置响应头;
- Filter设置
- Spring注解@CrossOrigin
从调用方解决
NginX、Apache配置反向代理(对客户端透明)