同源策略及限制
同源策略的概念
同源: http 协议, 域名, 端口三者均相同
同源策略是用来限制在一个源上加载的文档或脚本与来自另一个源的资源进行交互. 这是一个用于隔离潜在恶意文件的关键的安全机制.
同源策略的限制
cookie localStorage indexDB 无法读取
dom 无法获得 ajax 请求不能发送
前后端通信的常见几种方式
Ajax(同源通信)
webSocket(协议不同的不同源通信)
CORS(用于支持不同源之间 ajax 通信的方法)
Ajax 通信
参考
Ajax 概念
Ajax(Async JavaScript And XML) 是一种依赖 CSS/html/JAVASCRIPT 等现有技术使用 XMLHttpRequest
对象发送 http 请求并接受响应的一种技术方案
实现一个 Ajax
/**
* {string} param.url
* {string} param.type? || 'get'
* {object} param.data
* {function} param.success
* {function} param.error
*/
var ajax = function(param) {
var xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")
var type = (param.type || 'get').toUpperCase()
var url = param.url
if(!url) return
var data = param.data
var dataArr = []
for (var k in data) {
dataArr.push(k + '=' + data[k])
}
if (type === 'GET') {
url = url + '?' + dataArr.join('&')
xhr.open(type, url)
xhr.send()
}
if (type === 'POST') {
xhr.open(type, url)
xhr.setRequestHeader("Content-type", "application/x-www.form-urlencoded")
xhr.send(dataArr.join('&'))
}
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 304) {
var res
if (param.success && typeof param.success === 'function') {
res = xhr.responseText
if (typeof res === 'string') {
res = JSON.parse(res)
param.success.call(xhr, res)
}
}
} else {
param.error.call()
}
}
}
}
跨域通信的几种方式
首先我们先给 host 设置几个子域名来模拟跨域
跨域代码示例
$ npm install
$ npm start
port: 3000
使用示例前记得设置本机 host
JSONP
jsonp 原理就是在页面上动态添加一个 script 标签, 给标签的 src 指定一个 url 路径并加上回调函数 query 参数, 发送给后端后, 后端利用需返回的数据和回调函数的 query 参数拼接成类似
handleJsonp({ a:1, b:2 })
的字符串返回前端, 前端定义的 handleJsonp 的函数会直接运行并处理 {a:1, b:2} 这个后端返回的数据
只能发送 GET 请求
可能会被注入恶意代码 callback=alter('111')
任何域都可以发送 jsonp 请求, 需进行验证, 如 token
// 前端代码
jsonpBtn.addEventListener('click',
function() {
const script = document.createElement('script') script.src = 'http://b.yang.com:3000/jsonp?callback=handleJsonp'document.head.appendChild(script)
// document.head.removeChild(script)
}) function handleJsonp(data) {
console.log(data)
}
// 后端代码
// JSONP
router.get('/jsonp',
function(req, res, next) {
let {
callback: cb
} = req.query const data = {
type: 'jsonp',
data: 'data'
}
cb = cb.replace(/\(/g, ''); // 替换掉 () 防止恶意代码注入
cb = cb.replace(/\)/g, '');
res.send(cb + '(' + JSON.stringify(data) + ')')
}) CORS
CORS(cross-origin resource sharing) 跨域资源共享, 是一种 ajax 跨域请求资源的方式, 普遍用于前后端分离开发环境
原理就在于 Access-Control-Allow-Origin 响应头, 它指定浏览器在何种域下发送的 ajax 请求服务器资源时可以跨域
服务器响应还可以设置其它 header:
1Access-Control-Allow-Methods: POST, GET, OPTIONS 表明服务器允许客户端使用 POST, GET 和 OPTIONS 方法发起请求
2Access-Control-Allow-Headers: X-PINGOTHER, Content-Type 表明服务器允许请求中携带字段 X-PINGOTHER 与 Content-Type
3Access-Control-Max-Age: 86400 表明该响应的有效时间为 86400 秒
4Access-Control-Allow-Credentials: true 表明跨域请求允许携带 cookie
MDN
// 前端代码
cors.addEventListener('click',
function() {
let reqHeaders = new Headers() reqHeaders.append('Content-Type', 'application/x-www-form-urlencoded') fetch('http://b.yang.com:3000/cors/', {
method: 'post',
headers: reqHeaders,
mode: 'cors',
body: 'post body'
}).then(function(response) {
console.log(response)
})
})
// 后端代码
// CORS
router.post('/cors',
function(req, res, next) {
// res.header('Access-Control-Allow-Origin', 'http://a.yang.com:3000')
res.header('Access-Control-Allow-Origin', '*') res.send('cors ok')
}) WebSocket
利用 websocket 协议进行前后端跨域通信
// 前端代码
var ws
socket.addEventListener('click', function() {
ws = new WebSocket(`ws://b.yang.com:3000/`)
ws.onmessage = (data) => console.log(data);
ws.onerror = () => console.log('WebSocket error');
ws.onopen = () => console.log('WebSocket connection established');
ws.onclose = () => console.log('WebSocket connection closed');
})
sendmsg.addEventListener('click', function() {
ws.send('send a msg')
})
// 后端代码
var express = require('express');
var app = express();
const WebSocket = require('ws')
var server = http.createServer(app);
const wss = new WebSocket.Server({ server })
wss.on('connection', (ws, req) => {
ws.on('message', message => {
console.log(message)
ws.send(message)
})
})
server.listen(3000)
降域 (使用 iframe)
// URL: http://a.yang.com:3000/a
<div class="ct">
<h1 > 使用降域实现跨域 </h1>
<div class="main">
<h4>URL: http://a.yang.com:3000/a</h4>
<input type="text" placeholder="http://a.yang.com:3000/a">
</div>
<iframe src="http://b.yang.com:3000/b" frameborder="0" ></iframe>
</div>
<script>
document.querySelector('.main input').addEventListener('input', function(){
console.log(location.host, this.value);
window.frames[0].document.querySelector('input').value = this.value;
})
document.domain = "yang.com"
</script>
// URL: http://b.yang.com:3000/b
<input id="input" type="text" placeholder="http://b.yang.com:3000/b">
<script>
document.querySelector('#input').addEventListener('input', function(){
console.log(location.host, this.value);
window.parent.document.querySelector('input').value = this.value;
})
document.domain = 'yang.com';
</script>
postMessage(使用 iframe)
//URL: http://a.yang.com:3000/c
< div class = "ct" > <h1 > 使用postMessage实现跨域 < /h1>
<div class="main">
<h4>URL: http:/ / a.yang.com: 3000 / c < /h4>
<input type="text" placeholder="http:/ / a.yang.com: 3000 / c ">
</div>
<iframe src="http: //localhost:3000/d" frameborder="0" ></iframe>
< /div>
<script>
var input = document.querySelector('.main input')
input.addEventListener('input', function(){
console.log('a.yang.com - input event value', this.value);
window.frames[0].postMessage(this.value,'*');
})
window.addEventListener('message',function(e) {
input.value = e.data
console.log('a.yang.com - message event value', e.data);
});
</script >
// URL: http://b.yang.com:3000/d
< input id = "input"type = "text"placeholder = "http://b.yang.com:3000/d" > <script >
var input = document.querySelector('#input') input.addEventListener('input',
function() {
console.log('b.yang.com - input event value', this.value);
window.parent.postMessage(this.value, '*');
}) window.addEventListener('message',
function(e) {
input.value = e.data console.log('b.yang.com - message event value', e.data);
}); < /script>/
其他 hack
改变 hash 值
来源: http://www.jianshu.com/p/03f97e8ab73c