前言
定义
浏览器为了保证用户信息的安全,防止恶意网站窃取数据,禁止不同域之间的js交互。对于浏览器而言,只要协议、域名、端口其中有一个不同就会触发同源策略,造成跨域,从而限制交互
cookie、storage、indexDB等不能获取
ajax不能发送请求、dom树无法获得
为什么要限制跨域访问
如果一个网页可以随意的访问另一个网站的资源,就有可能在用户完全不知情的情况下出现安全问题
浏览器出于安全问题,对同源请求放行,对异源请求限制,这些限制规则统称为同源策略,因为限制造成的开发问题,称之为跨域(异源)问题
对标签发出的跨域请求轻微限制,对 AJAX 发出的跨域请求严厉限制
方法
常用方法
跨域资源共享(CORS)
CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。
是一套机制,用于浏览器校验请求。只要服务器明确表示允许,则校验通过;服务器明确拒绝或没有表示,则校验不通过。CORS将请求分为两类,简单请求和预检请求:
简单请求
请求方法为GET、HEAD、POST
头部字段满足CORS安全规范
请求头的Content-Type为 text/plain、multipart/form-data、application/x-www-form-urlencoded
服务器响应头,要么同源,要么放行
预检请求
浏览器发送预请求
从哪个源发送的请求:Origin:http://my.com
请求方式:Access-Controt-Request-Method:POST
请求改变了哪些请求头:Access-Controt-Request-Headers:content-type
服务器进行响应
Access-Controt-Allow-Origin:http://my.com...,允许的源,可以多个
Access-Controt-Allow-Method:POST,GET...,允许的方式,可以多个
Access-Controt-Allow-Headers:content-type...,允许改变的请求头,可以多个
Access-Controt-Max-Age:86400,多少秒不用再次检验
jsonp
前端写一个函数,服务端调用函数传入参数,通过参数获取,根本不是AJAX
// 获取返回结果
function callback(resp){
console.log(resp);
}
// 向服务端请求js文件
function request(url){
// 生成一个 script 标签
const script = document.createElement('script')
// 标签引用 js 路径为填入路径
script.src = url
// 标签解析完成之后删除
script.onload = function(){
script.remove()
}
// 插入 body 中
document.body.appendChild(script)
}
Nginx代理跨域
差别
cors:需要服务器设置响应头
jsonp:需要服务器响应一段js代码,并且还要调用函数
方法
浏览器请求自己的服务器 proxy
代理服务器请求目标服务器 target
服务器之间没有跨域问题,目标服务器响应代理服务器
代理服务器返回数据
// 引入库
const express = require('express')
// 创建服务器
const app = express()
// 接受对路径 /hero 的 GET 请求
app.get('/hero',async (req,res)=>{
// 使用 cors 解决对代理服务器的跨域
res.header('assess-control-allow-origin','*')
// 响应一段测试文本
res.send('你好,我是代理服务器')
// 在这里请求目标服务器,然后返回给前端
})
// 监听9527端口
app.listen(9527,()=>{
console.log('服务器已启动');
})
扩展学习
Node中间件代理跨域
Vue项目一般使用此方法,就是对代理服务器的一种封装。
在vue.config.js文件中配置
module.export = {
...
devServer: {
proxy: {
[ process.env.VUE_APP_BASE_API ]: {
target: 'http://xxxx', // 代理跨域目标接口
ws: true,
changeOrigin: true,
pathRewrite: {
[ '^' + process.env.VUE_APP_BASE_API ] : ''
}
}
}
}
Node/Express中配置:
const express = require(\'express\')
const proxy = require('http-proxy-middleware')
const app = express()
app.use('/', proxy({
// 代理跨域目标接口
target: 'http: // xxxx:8080',
changeOrigin: true,
// 修改响应头信息,实现跨域并允许带cookie
onProxyRes: function(proxyRes, req, res) {
res.header('Access-Control-Allow-Origin', 'http://xxxx')
res.header('Access-Control-Allow-Credentials', 'true')
},
// 修改响应信息中的cookie域名
cookieDomainRewrite: 'www.domain1.com' // 可以为false,表示不修改
}));
app.listen(3000);
Websocket
WebSocket是HTML5标准中的一种基于TCP但是区别于HTTP的通信协议,因为是长连接,该协议不实行同源政策。以ws://(非加密)和wss://(加密)作为协议前缀。
因为WebSocket请求头信息中有Origin字段,表示请求源来自哪个域,服务器可以根据这个字段判断是否允许本次通信。
// 创建websocket
var socket = new WebSocket('ws://www.baidu.com');
// 发送消息
socket.send('hello WebSocket');
// 接收消息
socket.onmessage = function(event){
var data = event.data;
}
postMessage
是HTML5引入的一个全新的API:跨文档通信 API(Cross-document messaging)。 这个API为window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。
窗口之间传递消息
// 父窗口发起
window.opener.postMessage("我是来自a页面的","http://b.com")
// 子窗口接收
window.onmessage = function(e){
e = e || event;
console.log(e.data);//我是来自a页面的
}
// 子窗口发送
window.opener.postMessage('我是来自b页面的', 'http://a.com');
读取其他窗口LocalStorage
// 父窗口调用方法
var obj = { name: 'Jack' };
// 存入对象
window.parent.postMessage(JSON.stringify({key: 'storage', method: 'set', data: obj}), 'http://b.com');
// 读取对象
window.parent.postMessage(JSON.stringify({key: 'storage', method: "get"}), "*");
window.onmessage = function(e) {
// 判断消息是否来自a窗口
if (e.origin != 'http://a.com') return;
// "Jack"
console.log(JSON.parse(e.data).name);
}
b窗口接收消息,获取自己的localStorage之后再传递给a窗口
window.onmessage = function(e) {
// 消息来自自己,不予理会
if (e.origin !== 'http://b.com') return;
var payload = JSON.parse(e.data);
// 判断方法进行操作
switch (payload.method) {
case 'set':
localStorage.setItem(payload.key, JSON.stringify(payload.data));
break;
case 'get':
var parent = window.parent;
var data = localStorage.getItem(payload.key);
// 如果是获取,获取之后传递给a窗口
parent.postMessage(data, 'http://a.com');
break;
case 'remove':
localStorage.removeItem(payload.key);
break;
}
}
单向跨域
利用浏览器对标签发出的跨域请求轻微限制,进行单方面的跨域请求
标签发送get请求
- <script src=""></script>标签嵌入跨域脚本,语法错误信息只能在同源脚本中捕捉到。
- <link src="">标签嵌入CSS。由于CSS的松散的语法规则,CSS的跨域需要一个设置正确的Content-Type消息头。
<object>、<embed>、<applet>的插件。
@font-face引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts)。
- <iframe> 和 <iframe>载入的任何资源。站点可以使用X-Frame-Options消息头来阻止这种形式的跨域交互。
版权归原作者 风不在乎 所有, 如有侵权,请联系我们删除。