概述
定义
A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源"。所谓"同源"指的是"三个相同"。
- 协议相同
- 域名相同
- 端口相同
目的
是为了保证用户信息的安全,防止恶意的网站窃取数据。
限制范围
- Cookie、LocalStorage 和 IndexDB 无法读取。
- DOM 无法获得。
- AJAX 请求不能发送。
cookie
- 一级域名相同时,可通过设置相同的 document.domain 来设置两个页面的访问。(只适用于cookie 和 iframe窗口)
- 服务器在响应的时,设置domain 为一级域名,二级域名和三级域名不用做任何设置,都可以读取这个Cookie。
Set-Cookie: key=value; domain=.example.com; path=/
复制代码
AJAX
同源政策规定,AJAX请求只能发给同源的网址,否则就报错。规避的几种方法
- JSONP
- WebSocket
- CORS
JSONP
- JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。
- 它的基本思想是,网页通过添加一个 script 元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。
前端实现:
代码语言:javascript复制function jsonP (url, callbackName = 'callback') {
return new Promise((resolve, reject) => {
const scriptTag = document.createElement('script')
scriptTag.src = url `?cb=${callbackName}`
scriptTag.setAttribute('type', 'text/javascript')
document.body.appendChild(scriptTag)
window[callbackName] = function jsonPCallback (data) {
if (data) resolve(data)
else reject(new Error('no data'))
document.body.removeChild(scriptTag)
}
})
}
jsonP('http://localhost:3000/jsonp').then(res => {
this.res = JSON.stringify(res)
})
// 返回数据样例
/**/ typeof callback === 'function' && callback({"data":{"name":"Tom"}});
复制代码
服务端实现(以express为例):
代码语言:javascript复制// app.js 添加cb函数名字段,该参数为前端查询参数
app.set('jsonp callback name', 'cb')
// 直接使用jsonp返回即可
router.get('/jsonp', function(req, res) {
res.jsonp({data: {name: 'Tom'}})
});
// 前端请求实例:http://localhost:3000/jsonp?cb=callback
复制代码
websocket
WebSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。
基本实现(socket.io)
前端:
代码语言:javascript复制const socket = socketIo('http://localhost:3000/', {
query: {
auth: '123'
}
})
ocket.on('connect', () => {
this.connectStatus = `链接成功-${socket.id}`
})
socket.on('disconnect', () => {
this.connectStatus = '链接断开'
})
socket.on('connect_error', (err: Error) => {
this.connectStatus = `链接失败-${err.message}`
})
socket.on('Hi', (message: string) => {
this.message = message
})
复制代码
服务端实现:
代码语言:javascript复制const io = require('socket.io')({
path: '/socket.io',
serveClient: false
});
io.on('connect', (socket) => {
// 校验跨域源信息
if (!['http://localhost:8080'].includes(socket.handshake.headers.origin)) {
return socket.disconnect()
}
socket.emit('Hi', 'Hello');
})
io.attach(server, {
pingInterval: 10000,
pingTimeout: 5000,
cookie: false
});
复制代码
浏览器跨域资源共享-CORS
1. 概述
- 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制.
- 跨源资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。
- 规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法,浏览器必须首先使用 OPTIONS 方法发起一个预检请求,从而获知服务端是否允许该跨源请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
2. 添加的头部信息
请求首部信息
- Origin 请求源信息,即当前源信息。部字段表明预检请求或实际请求的源站。
- Access-Control-Request-Headers 用于预检请求,告诉服务器额外增加的首部字段。逗号分割
- Access-Control-Request-Method 用于预检请求,告诉服务器要使用的请求方法。
响应首部信息
- Access-Control-Allow-Origin 服务器允许跨域的源信息。
- Access-Control-Allow-Methods 预检请求响应,服务器允许跨域请求的方法。
- Access-Control-Expose-Headers 预检请求响应,服务器允许携带的头部信息,这里允许,前端才能读到响应的这些头部信息。
- Access-Control-Max-Age 预检请求响应,表示预检请求的结果可以缓存多久,单位秒。
- Access-Control-Allow-Credentials 指定了当浏览器的credentials设置为true时是否允许浏览器读取response的内容。当用在对预检测请求的响应中时,它指定了实际的请求是否可以使用credentials。
2. 访问控制场景
简单请求
这类请求不会触发预检请求
- 属于简单请求的方法: GET, HEAD, POST.
- 允许的首部字段: Accept, Accept-Language, Content-Language, Content-Type (需要注意额外的限制), DPR, Downlink, Save-Data, Viewport-Width, Width
- Content-Type 允许的值: text/plain, multipart/form-data, application/x-www-form-urlencoded
请求携带的首部信息:
代码语言:javascript复制Origin: http://foo.example
复制代码
请求响应首部信息:
代码语言:javascript复制Access-Control-Allow-Origin: http://foo.example
复制代码
非简单请求
非简单请求会在正式请求之前,发送一个预检请求,用来询问服务器,是否可以跨域请求以及允许的请求方法,头部字段等。
预检请求携带的首部信息:
代码语言:javascript复制Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
复制代码
预检请求响应首部信息:
代码语言:javascript复制Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Expose-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
复制代码
附带身份凭证请求
- XMLHttpRequest 的 withCredentials 标志设置为 true,从而向服务器发送 Cookies。
- 对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“*”。必须指定明确的、与请求网页一致的域名。
- Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。
- Access-Control-Allow-Credentials 响应请求需要携带为true,如果没有携带或者为false,简单请求浏览器将不会把响应内容返回给请求的发送者。预检请求会报错。