
同源策略
-
指的是浏览器对不同源的脚本或者文本的访问方式进行的限制。比如源a的js不能读取或者设置引入的源b的元素属性
-
同源的三要素:
- 相同的协议
- 相同的域名
- 相同的端口号
-
同源策略限制的不同之间的交互主要针对的js中的XMLHttpRequest等请求,下面这些情况是完全不受同源策略限制的
-
存在的意义:
- 为了保证使用者信息的安全,防止恶意网站篡改用户数据
ajax同源策略主要用来防止CSRF攻击。如果没有ajax同源策略,相当危险,我们发起的每一次HTTP请求都会带上请求地址对应的cookie,那么可以做如下攻击:- 1.用户登录了自己的银行页面 mybank.com , mybank.com 向用户的 cookie 中添加用户标识。
- 2.用户浏览了恶意页面 evil.com 执行了页面中的恶意ajax请求代码
- 3.evil.com 向 mybank.com 发起ajax HTTP请求,请求会默认把mybank.com 对应cookie 也同时发送过去
- 4.银行页面从发送的cookie 中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了
- 5.而且用于ajax在后台执行,用户无法感知这一过程
- 为了保证使用者信息的安全,防止恶意网站篡改用户数据
-
限制范围(非同源的网站之间)
- 无法共享cookie,localstorage,indexDB
- 无法操作彼此的dom元素
- 无法发送ajax请求
- 无法通过flash发送http请求
跨域
- 指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript事件的安全限制
-
域名,协议,端口有一个不同都会造成跨域。
localhost和127.0.0.1虽然都指向本机,但也属于跨域。
浏览器执行JavaScript脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行。
跨域解决办法:Jsonp、CORS、Nginx反向代理
script src 属性 进行 get 请求 js 文件, 不受同源策略限制
script 标签 只是在请求数据, js文本, php响应的也是js文本,所以, src 也可以引入 php 文件
浏览器会将获取过来的文本当成 js 来执行
-
1、JSONP:
jsonp其实算一种hack形式的请求
jsonp的本质其实是请求你一段js代码,是对静态文件资源的请求,所以并不遵循同源策略
因为是对静态文件资源的请求,所以jsonp只支持get请求,不支持POST请求
ajax的核心是通过XMLHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本。
jsonp的诞生
1.首先,因为ajax无法跨域,然后开发者就有所思考
2.其次,开发者发现,<script>标签的src属性是可以跨域的,把跨域服务器写成 调用本地的函数,回调数据回来不久好了?
3.json刚好被js支持(object)
4.调用跨域服务器上动态生成的js格式文件(不管是什么类型的地址,最终生成的返回值都是一段js代码)
5.这种获取远程数据的方式看起来非常像ajax,但其实并不一样,便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作jsonp
6.传递一个callback参数给跨域服务端,然后跨域服务端返回数据时会将这个callback参数作为函数名来包裹住json数据即可。- jsonp应用
1.服务端JSONP格式数据
如客户想访问 : http://www.jsonp.com/try/ajax/jsonp.php?jsonp=callbackFunction。
假设客户期望返回JSON数据:["customername1","customername2"]。
真正返回到客户端的数据显示为: callbackFunction(["customername1","customername2"])。
服务端文件jsonp.php代码为:
<?php
header('Content-type: application/json');
//获取回调函数名
$callback = $_GET['callback'];
//json数据
$json_data = '["customername1","customername2"]';
//输出jsonp格式的数据
echo $callback . "(" . $json_data . ")";
?>
2.客户端实现 callbackFunction 函数
<script type="text/javascript">
function callbackFunction(result, methodName) {
var html = '<ul>';
for (var i = 0; i < result.length; i++) {
html += '<li>' + result[i] + '</li>';
}
html += '</ul>';
document.getElementById('divCustomers').innerHTML = html;
}
</script>
3.客户端页面完整代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JSONP 实例</title>
</head>
<body>
<div id="divCustomers"></div>
<script type="text/javascript">
function callbackFunction(result, methodName) {
var html = '<ul>';
for (var i = 0; i < result.length; i++) {
html += '<li>' + result[i] + '</li>';
}
html += '</ul>';
document.getElementById('divCustomers').innerHTML = html;
}
</script>
<script type="text/javascript" src="http://www.jsonp.com/try/ajax/jsonp.php?callback=callbackFunction"></script>
<!-- script标签可以是动态生成,放到事件中可以触发事件才获取数据
// 动态创建 script 标签, 请求数据
var script = document.createElement("script");
// 设置src请求数据
script.src = "http://www.jsonp.com/try/ajax/jsonp.php?callback=callbackFunction";
// 将 script 添加到 头部中去
document.head.appendChild( script );-->
</body>
</html>
4.jQuery 使用 JSONP
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JSONP 实例</title>
<script src="http://cdn.static.runoob.com/libs/jquery/1.8.3/jquery.js"></script>
</head>
<body>
<div id="divCustomers"></div>
<script>
$.getJSON("http://www.jsonp.com/try/ajax/jsonp.php?callback=?", function(data) {
var html = '<ul>';
for (var i = 0; i < data.length; i++) {
html += '<li>' + data[i] + '</li>';
}
html += '</ul>';
$('#divCustomers').html(html);
});
</script>
</body>
</html>
- 2、跨域资源共享(CORS):
普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。
1.前端设置
// 原生ajax
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
// 前端设置是否带cookie
xhr.withCredentials = true;
xhr.open('post', 'http://www.domain2.com:8080/login', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('user=admin');
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
}
-------------------------
// jQuery
$.ajax({
...
xhrFields: {
withCredentials: true // 前端设置是否带cookie
},
crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含cookie
...
});
2.后台设置
/*
* 导入包:import javax.servlet.http.HttpServletResponse;
* 接口参数中定义:HttpServletResponse response
*/
response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com"); // 若有端口需写全(协议+域名+端口)
response.setHeader("Access-Control-Allow-Credentials", "true");
- 3、nginx反向代理接口跨域
跨域原理: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
nginx具体配置:
#proxy服务器
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http: //www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com;#修改cookie里域名
index index.html index.htm;#当用webpack - dev - server等中间件代理接口访问nignx时, 此时无浏览器参与, 故没有同源限制, 下面的跨域配置可不启用
add_header Access - Control - Allow - Origin http: //www.domain1.com; #当前端只跨域不带cookie时,可为*
add_header Access - Control - Allow - Credentials true;
}
}
1.前端设置:
var xhr = new XMLHttpRequest();
// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;
// 访问nginx中的代理服务器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();
2.nodejs后台:
var http = require('http');
var server = http.createServer();
var qs = require('querystring');
server.on('request', function(req, res) {
var params = qs.parse(req.url.substring(2));
// 向前台写cookie
res.writeHead(200, {
'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly:脚本无法读取
});
res.write(JSON.stringify(params));
res.end();
});
server.listen('8080');
console.log('Server is running at port 8080...');
- 4、webpack-dev-server配置proxy
webpack 内置了 http-proxy-middleware 可以解决 请求的 URL 代理的问题
配置:
module.exports = {
devtool: 'cheap-module-source-map',
entry: './app/js/index.js'
output: {
path: path.resolve(__dirname, 'dev'),
// 所有输出文件的目标路径
filename: 'js/bundle.js',
publicPath: '/',
chunkFilename: '[name].chunk.js'
},
devServer: {
contentBase: path.resolve(__dirname, 'dev'),
publicPath: '/',
historyApiFallback: true,
proxy: {
// 请求到 '/device' 下 的请求都会被代理到 target: http://debug.xxx.com 中
'/device/*': {
target: 'http://debug.xxx.com',
secure: false, // 接受 运行在 https 上的服务
changeOrigin: true
}
}
}
}

网友评论