同源策略SOP(Same origin policy)是一种约定(即"协议+域名+端口"三者相同),它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到攻击。协议,域名,端口一种不同都是不同源,即跨域了
跨域会有如下限制:
1.Cookie、LocalStorage 和 IndexDB 无法读取
2.DOM 和 Js对象无法获得
- AJAX 请求不能发送
-
解决跨域的方法:
一、通过jsonp跨域
1.原生实现
js、css,img等静态资源不受跨域的限制,可以通过动态创建script,来解决跨域问题.
下面这个html页面和需要访问的php文件在同一协议,同一域名,不同端口下,所以是跨域请求
- index.html
<body>
<a>某个页面</a><input type="button" value="想通过ajax去请求数据">
<script>
function getInfo(obj){
//得到数据 解析到页面
console.log(obj);
//解析数据
}
document.querySelector("input").onclick = function(){
//XMLHttpRequest 去请求会有跨域限制,所以下面注释的这种方法不能使用
// var xhr = new XMLHttpRequest();
// xhr.open("get","http://localhost/getData.php");
// xhr.send(null);
// xhr.onreadystatechange = function(){
// if(xhr.readyState === 4 && xhr.status === 200){
// var data = xhr.responseText;
// console.log(data);
// }
// }
var script = document.createElement("script");
script.src = "http://localhost/getData.php?callback=getInfo";//传参一个回调函数名给后端,携带后端数据再执行前端的这个回调函数
document.body.appendChild(script); //让这个标签去发送请求 必须把这个标签挂载到页面上
}
</script>
</body>
- getData.php
<?php
$Info = $_GET['callback']; //getInfo函数
//向客户端输出 getInfo("GetData");
//组拼一个getInfo("GetData")
echo $Info."("."'GetData'".")";
?>
![](https://img.haomeiwen.com/i9251733/847c3bc5cae5b66c.png)
![](https://img.haomeiwen.com/i9251733/21c221c3714bf249.png)
![](https://img.haomeiwen.com/i9251733/21bba0b39d1d6672.png)
2. jquery ajax
$.ajax({
url: 'http://localhost/getData.php',
type: 'get',
dataType: 'jsonp', // 请求方式为jsonp
jsonpCallback: "getInfo", // 自定义回调函数名
data: {}
});
jsonp缺点:
1、只能实现get一种请求:
因为document.createElement('script') 生成一个 script 标签,然后插 body 里。在这里根本没有设置请求格式的余地。(script标签只能进行get请求)
2、只支持跨域 HTTP 请求这种情况,不能解决不同域的两个页面之间如何进行 JavaScript 调用的问题
3、调用失败的时候不会返回各种 HTTP 状态码。
4、安全性,万一假如提供 JSONP 的服务存在页面注入漏洞,即它返回的 javascript 的内容被人控制的
二、 跨域资源共享(CORS)
普通跨域请求:服务端只需要设置Access-Control-Allow-Origin
即可,前端无须设置,若要带cookie请求:前后端都需要设置后端需要设置 Access-Control-Allow-Credentials
为true,前端需要设置withCredentials
为true。
需注意的是:由于同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,而非当前页。
在 cors 中会有简单请求和复杂请求的概念
-
简单请求:不会出发cors预检请求
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
凡是不同时满足上面两个条件,就属于非简单请求。
cors的工作原理:
1、浏览器先判断是简单请求还是复杂请求。
2、如果是复杂请求,在正式请求前浏览器会用OPTIONS
方法先发送一个预检请求,OPTIONS
是HTTP/1.1
协议中定义的方法。
- 该方法不会对服务器资源产生影响,预检请求中同时携带下面的首部字段:
Access-Control-Request-Method
: 这个字段表明了请求的方法;
Access-Control-Request-Headers
: 这个字段表明了这个请求的 Headers;
Origin
: 这个字段表明了请求发出的域。 - 服务器接收到请求后允许携带的信息类型:
Access-Control-Allow-Origin
: 能够被允许发出这个请求的域名,也可以使用*来表明允许所有域名;
Access-Control-Allow-Methods
: 用逗号分隔的被允许的请求方法的列表;
Access-Control-Allow-Headers
: 用逗号分隔的被允许的请求头部字段的列表;
Access-Control-Max-Age
: 这个预检请求能被缓存的最长时间,在缓存时间内,同一个请求不会再次发出预检请求。
3、如果是简单请求,浏览器直接发出cors请求。会在头部信息自动的添加origin字段,来说明子来自哪个源。服务器拿到请求之后,在回应时对应地添加Access-Control-Allow-Origin
字段,如果 Origin 不在这个字段的范围中,那么浏览器就会将响应拦截。
1.前端设置
(1)原生ajax
// 前端设置是否带cookie
xhr.withCredentials = true;
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
// 前端设置是否带cookie
xhr.withCredentials = true;
xhr.open('post', 'http://localhost/index.php', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(null);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
};
(2)jquery ajax
$.ajax({
...
xhr: {
withCredentials: true // 前端设置是否带cookie
},
crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含cookie
...
});
2.后台设置
若后端设置成功,前端浏览器控制台则不会出现跨域报错信息,反之,说明没设成功。
(1)php
//在php中设置客跨域访问
if (config('app.environment') == 'local') { // 如果是本地环境就允许跨域访问
header('Access-Control-Allow-Origin: *');
//上面第一行说到的Access-Control-Allow-Origin有多种设置方法:
//(1)设置*是最简单粗暴的,但是服务器出于安全考虑,肯定不会这么干,而且,如果是*的话,浏览器将不会发送cookies,即使你的XHR设置了withCredentials
//(2) 指定域,一般的系统中间都有一个nginx,所以推荐这种,例如:header("Access-Control-Allow-Origin","http://localhost:3000");
//(3)动态设置为请求域,多人协作时,多个前端对接一个后台,这样很方便
//withCredentials:表示XHR是否接收cookies和发送cookies,也就是说如果该值是false,响应头的Set-Cookie,浏览器也不会理,并且即使有目标站点的cookies,浏览器也不会发送。
header('Access-Control-Allow-Credentials: true'); //是否允许后续请求携带认证信息(cookies),该值只能是true,否则不返回
//预检请求(参考文章:http://www.php.cn/div-tutorial-378889.html) --- 一般不用设置
//与简单请求不同的是,option请求多了2个字段:
//Access-Control-Request-Method:该次请求的请求方式
//Access-Control-Request-Headers:该次请求的自定义请求头字段
//Access-Control-Max-Age 表明该响应的有效时间为 86400 秒,也就是 24 小时。在有效时间内,浏览器无须为同一请求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最大有效时间,将不会生效
//预检结果缓存时间,也就是上面说到的缓存啦
//'Access-Control-Max-Age: 86400'
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); //允许的请求类型
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept"); // 允许的请求头字段
}
if (Request::isOptions()) { // 判断是否为OPTIONS请求
exit; //因为预检请求第一次是发送OPTIONS请求返回了响应头的内容,但没有返回响应实体response body内容。这个我们不处理业务逻辑,第二次接收的get或post等才是实质的请求返回我们才处理
}
}
三、nginx反向代理
nginx是一种高性能的反向代理服务器
nginx拿到客户端的请求,将请求转发给其他的服务器,处理完成后返回给nginx,nginx服务器会交给客户端,主要的场景是维持服务器集群的负载均衡。
server {
listen 80;
server_name local.test;
location /api {
proxy_pass http://localhost:8080;
}
}
当客户访问local.test/api的时候,nginx会将服务转发到http://localhost:8080上,当相应返回后,将相应内容发给客户端
四、webpack中的proxy代理
proxy: { //代理
'/api': {
target: 'http://localhost:3000',
},
}
当访问/api/xxx的时候,会转向访问http://localhost:3000/xxx
网友评论