1,什么是同源?
如果两个页面的协议,端口和域名都相同,则两个页面具有相同的源。(协议+IP+端口)
2,什么是同源策略?
同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互,这是一个用于隔离潜在恶意文件的重要机制。
例:在8000端口,通过ajax用get的方式访问8001的端口的某个路由,虽然8001的服务器有响应,但是浏览器给拦截了,不让拿到。
(用8000端口返回的页面,通过ajax的get请求,访问8001端口,浏览器会拒绝)
Failed to load http://127.0.0.1:8001/anhao:
无法加载到。。。
(Redirect from 'http://127.0.0.1:8001/anhao' to 'http://127.0.0.1:8001/anhao/' has been blocked by CORS policy:
从。。。重定向到。。。已经被CORS策略阻塞)
No 'Access-Control-Allow-Origin' header is present on the requested resource.
目前在请求源没有↑头
Origin 'http://127.0.0.1:8000' is therefore not allowed access.
因此,↑的起源是不允许访问的
Access to XMLHttpRequest at 'http://127.0.0.1:8001/anhao/' from origin 'http://127.0.0.1:8002'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
3,不受同源策略限制的
1,页面中的链接(页面上写个http://wwww.baidu.com),重定向以及表单提交(form中action='http://www.sougou.com/web')
2,通过script标签引入跨域资源是不受同源策略限制的,但是js不能读写加载的内容。如<script src=""></script>,<img>,<link>,<iframe>
3,服务器返回响应头时加了[Access-Control-Allow-Origin: *] ---> [访问控制允许原点:*]
4,解决跨域的几种方式:
- 1,Ajax的jsonp(需要后端的返回格式支持jsonp的格式。jsonp只支持get请求)
后端
def fun1(request):
callback = request.GET.get('callback')
res = {'code': 'fun1你猜', 'msg':'fun1嘿嘿'}
data_str = json.dumps(res)
return HttpResponse('{}({})'.format(callback,data_str))
def fun2(request):
callback = request.GET.get('callback')
res = {'code': 'fun2不告诉你', 'msg':'fun2哈哈哈哈'}
data_str = json.dumps(res)
return HttpResponse('{}({})'.format(callback,data_str))
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button id="b1">点我,测试jsonp方法1</button>
<button id="b2">点我,测试jsonp方法2</button>
<button id="b3">点我,测试jsonp方法3</button>
</body>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
{#【方法1 】#}
<script>
$("#b1").click(function () {
$.getJSON("http://127.0.0.1:8001/fun1/?callback=?", function (res) { // callback=? 随机生成了一个函数名。
console.log(res);
})
})
</script>
{#【方法2 】#}
<script>
function ffuu(res){
console.log("我是谁?我在哪里?我要干什么?");
console.log(res);
}
$("#b2").click(function () {
$.ajax({
url: "http://127.0.0.1:8001/fun2/",
dataType: "jsonp", // 指定此次请求是jsonp形式
jsonp: "callback", // 回调函数的URL参数
jsonpCallback: "ffuu" // 回调函数的函数名
})
})
</script>
{#【方法3 】#}
<script>
$("#b3").click(function () {
$.ajax({
url: "http://127.0.0.1:8001/fun2/",
dataType: "jsonp", // 指定此次请求是jsonp形式
success:function (res) {
console.log(res);
}
})
})
</script>
</html>
- 2,CORS,在django的中间件添加响应头
from django.utils.deprecation import MiddlewareMixin
class MyCore(MiddlewareMixin): # 解决跨域问题
def process_response(self, request, response):
response["Access-Control-Allow-Origin"] = '*' # 解决简单请求的跨域问题
if request.method == 'OPTIONS': # 解决复杂请求的跨域问题
response["Access-Control-Allow-Headers"] = 'Content-Type'
response["Access-Control-Allow-Methods"] = 'POST,DELETE,PUT'
return response
- 3,在nginx配置反向代理
其实是将url设计成了同源,对于浏览器来说,访问的就是同源服务器上的一个url。
而nginx通过检测url前缀,把http请求转发到后面真实的物理服务器。
127.0.0.1:80/index 访问vue单页面
127.0.0.1:80/api/... 通过nginx反向代理,转发至后端服务器,请求数据接口。
location / {
try_files \$uri $uri/ @router; # 指向下面的 @router否则会出现 404
index index.html;
}
location /api {
include /etc/nginx/uwsgi_params;
uwsgi_pass 10.0.0.11:9000;
}
}
- 4,在vue项目开发中,可以使用配置文件解决跨域问题(
vue.config.js
)
module.exports = {
publicPath: '/eventmanage/',
assetsDir: '',
outputDir: 'eventmanage-dist',
devServer: {
port: 8866,
host: '0.0.0.0',
open: true,
proxy: {
'/event_api': {
target: 'http://127.0.0.1:8888/',
ws: true,
changeOrigin: true,
},
'/evts': {
target: 'http://127.0.0.1:8000/',
ws: true,
changeOrigin: true,
},
}
}
};
5,OPTION请求,预检
1,浏览器将CORS请求分成两类:简单请求和复杂请求。
2,只要同时满足以下两大条件,就属于简单请求:
1,请求方式是以下三种方法之一:
HEAD GET POST
2,HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
3,但凡不同时满足以上两个条件,就属于非简单请求。浏览器对两种请求的处理,是不一样的。
4,复杂请求先预检,发送一个option请求,所以当在中间件解决跨域问题时需要判断if request.method == 'OPTION':
就证明是复杂请求。
5,简单请求和非简单请求的区别?
简单请求:一次请求
非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。
6,关于“预检”
- 请求方式:OPTIONS
- “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
=> 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
Access-Control-Request-Method
=> 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
Access-Control-Request-Headers
7,vue中使用axios发送ajax时,数据中的data:{user:that.user,pwd:that.pwd},
会自动封装成json格式,所以是复杂请求。
6,Ajax如何跨域获取cookie
首先需要访问的url中,在本地浏览器有cookie记录,然后加上这段配置,才可跨域携带cookie访问。
$.ajax({
type: 'POST',
url: url ,
data: JSON.stringify({query_id: expand_id, type: 'no_use'}),
contentType: 'application/json;charset=UTF-8',
dataType: "json",
crossDomain: true, //设置跨域为true
xhrFields: {
withCredentials: true //默认情况下,标准的跨域请求是不会发送cookie的
},
success: function success(response) {
},
error: function error(err) {
console.log(err);
}
});