1. 前言
1.1 什么是跨域
跨域指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
1.1.1 跨域对比表
当前页面url | 被请求页面url | 是否跨域 | 原因 |
---|---|---|---|
http://www.test.com/ | http://www.test.com/index.html | 否 | 同源(协议、域名、端口号相同) |
http://www.test.com/ | https://www.test.com/index.html | 跨域 | 协议不同(http/https) |
http://www.test.com/ | http://www.baidu.com/ | 跨域 | 主域名不同(test/baidu) |
http://www.test.com/ | http://blog.test.com/ | 跨域 | 子域名不同(www/blog) |
http://www.test.com:8080/ | http://www.test.com:7001/ | 跨域 | 端口号不同(8080/7001) |
1.2 解决方案
-
JSONP (不推荐)
# 通过<script> 标签的src属性不受同源策略方式 (只能使用GET请求) <script src="https://www.baidu.com/s?wd=百度百科" />
-
代理服务器 (NGINX)
<!-- 通过nginx的转发机制完成跨域 --> server { listen 80; server_name b.b.com; location /Api { # 反向代理 proxy_pass http://b.b.com:81/Api; index index.html index.htm; } }
-
cors
通过服务端设置允许跨域的请求头
response.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS"); // 设置本次预请求的预检有效期 单位毫秒 response.setHeader("Access-Control-Max-Age", "3600"); // 指定该响应的资源是否被允许与给定的origin共享 * 允许所有域都具有访问资源的权限。 response.setHeader("Access-Control-Allow-Origin", "*"); // 设置本次预检请求允许正式请求中的设置的header response.setHeader("Access-Control-Allow-Headers", "Content-Type, x-requested-with, X-Custom-Header, Authorization,access-control-allow-credentials,Access-Control-Allow-Methods," + "X_Requested_With,Access-Control-Allow-Headers,Access-Control-Allow-Origin");
2. 解决ueditor上传附件跨域问题
2.1 前端
2.1.1 修改视频上传请求参数
路径: public\ueditor\dialogs\video\video.js
# 找到 uploadBeforeSend 定义的实现
# 添加以下请求头:
## header["Access-Control-Allow-Headers"] = "X-Requested-With";
## header["Access-Control-Allow-Methods"] = "POST,OPTIONS";
## header["Access-Control-Allow-Credentials"] = "true";
uploader.on('uploadBeforeSend', function (file, data, header) {
//这里可以通过data对象添加POST参数
header['X_Requested_With'] = 'XMLHttpRequest';
header["Access-Control-Allow-Headers"] = "X-Requested-With";
header["Access-Control-Allow-Methods"] = "GET,POST,OPTIONS";
header["Access-Control-Allow-Credentials"] = "true";
// HaoChuan9421
if(editor.options.headers && Object.prototype.toString.apply(editor.options.headers) === "[object Object]"){
for(var key in editor.options.headers){
header[key] = editor.options.headers[key]
}
}
});
2.1.2 修改图片上传请求参数
路径: public\ueditor\dialogs\image\image.js
# 找到 uploadBeforeSend 定义的实现
# 添加以下请求头:
## header["Access-Control-Allow-Headers"] = "X-Requested-With";
## header["Access-Control-Allow-Methods"] = "POST,OPTIONS";
## header["Access-Control-Allow-Credentials"] = "true";
uploader.on('uploadBeforeSend', function (file, data, header) {
//这里可以通过data对象添加POST参数
header['X_Requested_With'] = 'XMLHttpRequest';
header["Access-Control-Allow-Headers"] = "X-Requested-With";
header["Access-Control-Allow-Methods"] = "PUT,POST,GET,DELETE,OPTIONS";
header["Access-Control-Allow-Credentials"] = "true";
// HaoChuan9421
if(editor.options.headers && Object.prototype.toString.apply(editor.options.headers) === "[object Object]"){
for(var key in editor.options.headers){
header[key] = editor.options.headers[key]
}
}
});
2.2 后端
2.2.1 设置允许options请求方法
vim application.properties
# 允许options请求方法
spring.mvc.dispatch-options-request=true
2.2.2 添加过滤器
/**
* @description: 解决跨域问题过滤器
* @author: liqr
* @create Date: 2020/11/28 16:07
* @modify User:
* @modify Date:
* @modify Description:
*/
@Component
public class CorsFilter implements Filter {
/**
* options请求方法
*/
final String OPTIONS_METHODS = "OPTIONS";
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
// 如果是options嗅探方法则直接设置响应头返回
if (Objects.equals(((HttpServletRequest) req).getMethod(), OPTIONS_METHODS)) {
HttpServletResponse response = (HttpServletResponse) res;
// 设置本次预检请求的应答中明确了客户端所要访问的资源允许使用的方法或方法列表。
response.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS");
// 设置本次预请求的预检有效期 单位毫秒
response.setHeader("Access-Control-Max-Age", "3600");
// 指定该响应的资源是否被允许与给定的origin共享 * 允许所有域都具有访问资源的权限。
response.setHeader("Access-Control-Allow-Origin", "*");
// 设置本次预检请求允许正式请求中的设置的header
response.setHeader("Access-Control-Allow-Headers",
"Content-Type, x-requested-with, X-Custom-Header, Authorization,access-control-allow-credentials,Access-Control-Allow-Methods," +
"X_Requested_With,Access-Control-Allow-Headers,Access-Control-Allow-Origin");
return;
}
chain.doFilter(req, res);
}
}
3. cors 扩展
3.1 cors 简介
1. 整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
2. 要实现CORS通信, 服务器端 必须实现CORS接口。
3.2 cors分类
cors请求又分为以下两类:
3.2.1 简单请求 (simple request)
- 允许得请求方法
- HEAD
- GET
- POST
- 允许得头部信息
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
请求头描述:
-
Access-Control-Request-Method
-
Access-Control-Request-Method : access-control-allow-methods,x_requested_with 表明浏览在发送cors请求时 会使用到的方法列表(如: POST,GET)
-
-
Access-Control-Request-Headers
-
Access-Control-Request-Headers : POST 表明浏览在发送cors请求时 会使用到的请求头列表 (如: x_requested_with)
-
3.2.2 非简单请求 (not-so-simple request)
除了以上简单请求允许的方法以及允许的请求头以外的请求都属于非简单请求
如:
- PUT
- DELETE
- 。。。
所有的自定义的请求头中字段都会让浏览器认为该次请求为 非简单请求
3.2.2.1 OPTIONS(预检请求) 定义
当浏览器认为本次请求为 非简单请求 时, 会默认先发送一个OPTIONS (预检请求), 向服务端询问是否允许进行跨域请求。
3.2.2.2 预检请求的响应
-
Access-Control-Allow-Headers
-
"Access-Control-Allow-Headers": "Content-Type, x-requested-with, X-Custom-Header, Authorization,access-control-allow-credentials,Access-Control-Allow-Methods,X_Requested_With,Access-Control-Allow-Headers,Access-Control-Allow-Origin" 设置本次预检请求允许正式请求中的设置的header
-
-
Access-Control-Allow-Methods
-
"Access-Control-Allow-Methods": "POST,GET,OPTIONS" 设置本次预检请求的应答中明确了客户端所要访问的资源允许使用的方法或方法列表。
-
-
Access-Control-Allow-Origin
-
"Access-Control-Allow-Origin" : "*" 指定该响应的资源是否被允许与给定的origin共享 * 允许所有域都具有访问资源的权限。
-
-
Access-Control-Max-Age
-
"Access-Control-Max-Age" : "3600" 设置本次预请求的预检有效期 单位毫秒
-
-
Access-Control-Allow-Credentials
-
设置是否允许发送发送Cookie给服务端
-
3.3 请求示例
预检请求
正式请求
3.4 其他
- 只要发送的 OPTIONS (预检请求) 服务端通过后, 后面的每次请求都会转成 简单请求 (simple request)
ntials**
-
设置是否允许发送发送Cookie给服务端
3.4 其他
- 只要发送的 OPTIONS (预检请求) 服务端通过后, 后面的每次请求都会转成 简单请求 (simple request)
- 如果 OPTIONS 请求中返回的信息 不包含CORS有关的头部字段 , 则抛出异常, 该异常会被 XMLHttpRequest 的 onerror 进行捕获。