被调用方解决跨域是指在 HTTP 响应头中增加指定的字段, 允许调用方调用
可以在两种地方增加
- apache/nginx(HTTP 服务器)
- tomcat(应用服务器)
浏览器如何判断跨域?
仔细观察可以发现, 跨域请求的请求头中多了一个 Origin 字段, 这个字段的值是当前域的信息
浏览器发现请求是跨域的时候, 会在当前请求的请求头中添加一个当前域的信息的字段, 等返回后检查响应头中有没有相应的信息
如果没有, 则报错
浏览器先执行还是先判断?
简单请求先执行后判断, 非简单请求先判断后执行
何为简单请求?
简单请求就是请求 header 里:
1. 无自定义请求头
2.Content-Type 为以下几种:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
常见方法为: GETHEADPOST
非简单请求:
1.put,delete 方法的 aja 请求
2. 发送 json 格式的 ajax 请求
3. 带自定义头的 ajax 请求
对于非简单请求, 浏览器会在 htpp 的请求头中添加一条字段:
Access-Control-Request-Headers: Content-Type
即: 询问服务器是否允许这个 Content-Type 这个请求头
此时, 为了使得非简单请求通过预检, 则需要在响应头中添加以下字段:
Access-Control-Allow-Headers: Content-Type
即支持 Content-Type
执行简单请求的跨域代码, 发现后台服务器正常返回数据, 在 chrome 的 network 中也可以找到返回的数据
所以, 简单请求浏览器先执行然后判断
当非简单请求发生跨域时, 浏览器检测到非简单的跨域请求, 会自动发出一个 OPTION 请求, 就是所谓的预检命令, 当预检通过的时候, 才会把真正的请求发出去
即非简单请求在 network 中可以看到两次请求, 一次 OPTION 请求, 一次真正的请求
由于非简单命令太过耗费时间 (请求两次), 所以可以在响应请求头中添加一个字段:
Access-Control-Max-Age: 3600 (单位为秒)
即在一个小时内, 该请求可以缓存
此时, 在第一次访问这个方法需要请求两次, 而第二次则只需要请求一次, 去除了预检命令
但是, 此时去除 Access-Control-Max-Age 与 Access-Control-Allow-Headers, 则请求依然会成功, 因为该请求已经被缓存
一: 被调用方 - Filter 解决方案
Filter 解决方案主要是在 http 响应头上添加如下信息:
Access-Control-Allow-Origin: 域名
只要域名包含请求头中 Origin 字段的值, 则支持当前跨域
如果 Access-Control-Allow-Origin: * , 则支持所有域名访问
该字段必填
如果加上 Access-Control-Allow-Methods: 请求方法, 则表示只支持特定的请求方法进行跨域
如 Access-Control-Allow-Methods: GET 则表示只支持 GET 方法进行跨域
如果 Access-Control-Allow-Methods: * 则表示支持所有方法进行跨域, 此时, 写了和没写一样
此字段可选, 非必填
二: 被调用方 - Nginx 解决方案
使用 Nginx 配置代理服务器, 前端请求地址改为 Nginx 配置代理服务器
在 Nginx 中配置 Filter 解决方案中类似的响应头可以实现
三: 被调用方 - Apache 解决方案
类似于 Nginx 解决方案, 只不过使用 Apache 代替 Nginx
四: 被调用方 - Spring 框架解决方案
Spring 框架中有个名为 @CrossOrigin 的注解, 该注解被用来处理跨域请求
在使用 Spring 框架的基础上, 在代码中使用 @CrossOrigin 注解, 可以解决跨域问题
@CrossOrigin 既可以加在类中, 也可以加在具体的方法中, 加在类中代表着这个类所有的方法支持跨域, 加在方法中代表着这个方法支持跨域
来源: http://www.bubuko.com/infodetail-2520779.html