cookie?同源策略?跨域?跨域解决方法?
在讲解跨域之前,要先讲一下跨域的出现是出于浏览器的同源策略限制,以及cookie。
1 cookie
当我们在访问网页的时候客户端(我们本地的电脑)会发送一个请求到服务器,服务器会保存用户状态,生成一个证书文件(就是cookie),并返回到客户端,存储在浏览器中,当我们使用同一个浏览器再次发出请求的时候,客户端就会将本地的cookie加上URL访问地址同是发送给服务器,服务器就会辨别用户状态(URL组成:协议://主机(域名):端口/路径,其中主机也指Internet上的域名)。
比如我们打开京东进行购物的时候,大家有没有想过为什么我们登录过一次账号之后你,在一段时间之内只要我们用同一浏览器打开网站就能自动登录,不用再重复的登录。这就是因为我们上一次登录的 时候,我们的电脑(客户端)发送了登录的请求,而服务器也生成了一个cookie文件返回给客户端存储在浏览器端,浏览器就会记住了你的登录信息,你的账号,密码,购物车信息等等,所以下一次打开京东,你就会发现你的账号已经登录,不需要再次登录。
总结,cookie文件可以用来存放用户的登录信息。只有第一次登录的时候访问数据库,只要设置了过期时间,下次再登录后直接读取cookie存储的登录信息,即可达到不登录就可以访问网页的效果。cookie的文件是实时变换的,到目前为止,我们对cookie应该有个大致的了解了。后面我将要写一篇关于session与cookie的区别的文章,也希望我们共同进步。
2 同源策略
首先解释一下什么是同源。
同源指的是两个网页的协议、端口(如果有)、域名相同,就是同源。同源策略是一种安全策略,是浏览器的一种行为。简单来说,同源策略就是为了防止数据污染,保证同一个浏览器中的不同网页之间不会出现信息泄露或者污染。同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。
每一个页面都有对应的协议、端口(http协议端口默认80,https协议端口默认433)、域名。当我们使用浏览器访问我们的资金账户的时候,有可能会在同意浏览器打开不同的页面,这个时候我们可以查看浏览器上的cookie文件,cookie文件中包含了资金账户的登录账号与密码,如果此时通过提取出cookie中的信息进行恶意访问资金账户网站。此时同源策略的重要性不言而喻(用于隔离潜在恶意文件的重要安全机制)。
我们在进行浏览一个页面的时候,客户端(就是我们本地的电脑)输入对应的URL就能访问对应的网页。比如:
https://hao.360.com/?a1111=12&name=tt
https 是网络协议
hao.360.com 是域名,或者127.0.0.1这种IP地址 也是属于域名
?a1111=12&name=tt 是参数
这个URL隐藏了端口
再比如:
https://10.10.32.110:8083/?name=tt
8083 是端口
3.跨域
简单来说:
两个网页的协议、端口(如果有)、域名相同,就是同源,只要其中一个不一样就是跨域了;
4.web跨域解决方法
4.1 jsonp
注意:
jsonp方式不支持POST的请求方式,就算你强制的使用POST方式,也会自动转为GET方式,如果后端直接设置了POST方式,直接没法请求。
4.1.1 原生js
首先我们要明白,在页面上直接发起一个跨域请求的ajax请求是不可以的,但是,在页面上引入不同域上的js脚本是可以的。所以思路就是要进行跨越请求的时候,在页面中插入js 脚本。
其实这样的请求方式我们无形之中就用过,只是没有意识到而已,比如在HTML文档中引入js文件:
<script src="./js/jquery-1.12.4.min.js"></script>
现在来说一下使用原生的js怎么实现跨域
思路:
使用<script src=" "></script>
实现:点击“跨域”的按钮,添加一个<script>
标签,用于发起跨域请求。注意:请求地址后面带一个回调函数的参数:&callback=showData (showData 是一个全局函数)
代码:
// 请求的跨域地址
let url = 'https://p.3.cn/prices/mgets?skuIds=J_5089253&type=1';
function showData(data){
console.log(data);
};
$(function(){
//点击“跨域”按钮时,在head标签中创建一个script标签,标签中的src属性值就是我们要跨域的地址url,并且加上&callback=showData
$("button").click(function(){
$("head").append(` <script src="${url}&callback=showData"></script>`);
});
});
4.1.2 jquery
使用原生的js确实能请求到数据,但是还是略显麻烦,现在看一下使用jQuery怎么实现jsonp的请求数据方式:
思想:
在ajax方法中配置:dataType:'jsonp'
实现代码:
// 请求的跨域地址
var url = 'https://p.3.cn/prices/mgets?skuIds=J_5089253&type=1';
$.ajax({
url: url,
dataType: 'jsonp', // 不加这个,就会报跨域请求失败。加了这个,就是以jsonp的形式来实现跨域
success: function (data) {//请求成功的回调函数,就和原生的showData一样的
console.log(data);
},
error: function (err) {//请求失败的回调函数
console.log(err);
}
});
jQuery使用自己集成好的ajax请求跨域,实际原理还是和原生使用<script>
脚本一样的。
4.2 node.js
router.all('*', function(req, res, next) {
//Access-Control-Allow-Origin是重点,就是它实现跨域的,它的参数有(*,指定域,动态设置),下面细说
res.header("Access-Control-Allow-Origin", "*");
//是否允许后续请求携带认证信息(cookies),该值只能是true,否则不返回
res.header('Access-Control-Allow-Credentials:true');
//允许的请求头字段
res.header("Access-Control-Allow-Headers", "X-Requested-With,content-type");
//允许请求的方式
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
//PHP版本的信息
res.header("X-Powered-By",' 3.2.1')
//请求内容类型
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
res.header("Access-Control-Allow-Origin", "*");
这段代码就是重点了:
1)* : 表示所有的意思,值得是允许所有的可以,一般处于安全考虑不使用它,并且,如果是*的话,游览器将不会发送cookies
,即使你的XHR
设置了withCredentials
。
XMLHttpRequest.withCredentials 属性是一个Boolean
类型,它指示了是否该使用类似cookies,authorization headers(头部授权)或者TLS客户端证书这一类资格证书来创建一个跨站点访问控制(cross-site Access-Control
)请求。
2)指定域:可以写指定的路径。比如:https://10.10.27.112:8083/p=12 ;推荐
3)动态设置:多人协作时,多个前端对接一个后台,这样很方便
4.3 vue
使用脚手架vue-cil的时候回自动生成一些目录与文件,在config目录下的index.js文件下进行配置跨域:
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {//修改的代码在这
"/api":{
target:"http://localhost:3000",//配置要跨域的地址
changeOrigin:true,// 是否允许跨域;
// 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
pathRewrite:{//路径重写
"^/api":"http://localhost:3000",// 替换target中的请求地址,使用的时候不用再重复写http://localhost:3000,直接使用/api就行
}
}
},
使用axios访问数据的时候:
// 确定基础url
const baseUrl = "/api";
// 获取一条菜单数据 编辑列表 get 方式
export const reqMuneinfo = params => {
return axios({
method: "get",
url: baseUrl + "/api/menuinfo",
params
});
};
// 提交修改菜单数据 post方式
export const reqMenuedit = data => {
return axios({
method: "post",
url: baseUrl + "/api/menuedit",
data: qs.stringify(data)//qs模块将hash访问方式的参数转为后端可识别的history形式
});
};