跨域携带身份凭证
场景需求分析
在前后端分离的情况下,假如异步请求的域名与当前页面所在域名不相同,也就是不同源的情况下,鉴于同源策略,会禁止向异源发起请求。这种情况下可通过跨域请求解决上述问题,而在跨域请求时,有些情况下会要求携带上身份凭证(例如cookie),下面就针对这一需求来详细解答。
代码&解析
- 服务器端,请求地址为http://www.test.me/1.php,文件名为1.php
<?php
// 1.php
echo json_encode(['status' => 1, 'msg' => '成功'], JSON_UNESCAPED_UNICODE);
- 客户端,入口地址为http://www.test1.me/1.html,文件名为1.html
// 注:此处的jq需自行下载放在当前代码所在目录
// 1.html
<script src="jquery-3.5.1.min.js"></script>
<script>
$.ajax({
url:'http://www.test.me/1.php',// 服务端地址
success:function(res){
console.log('res', res)
},
error:function(err_res) {
console.log('err_res', err_res)
}
})
</script>
分析:假设客户端代码入口地址A为http://www.test1.me/1.html,服务端地址B为http://www.test.me/1.php,1.html中直接请求1.php,打开浏览器console面板,会出现跨域请求报错,详情如下
为了解决跨域的问题,最简单直接的方法是在服务器段添加一个头部,代码如下
<?php
// 1.php
header('Access-Control-Allow-Origin:*');
echo json_encode(['status' => 1, 'msg' => '成功'], JSON_UNESCAPED_UNICODE);
此处设置的头部,意思是允许所有源的请求访问;再次打开http://www.test1.me/1.html,跨域报错就没了。
只不过以上的处理方式,虽然使得跨域获取数据不成问题,但是跨域的时候还没带上身份凭证,需要再添加一些东西,同样,先看代码
// 1.html
<script src="jquery-3.5.1.min.js"></script>
<script>
$.ajax({
url:'http://www.test.me/1.php',
crossDomain:true,
xhrFields:{
withCredentials: true
},
success:function(res){
console.log('res', res)
},
error:function(err_res) {
console.log('err_res', err_res)
}
})
</script>
对比一开始,多了crossDomain以及xhrFields两项,前者意思是进行跨域请求,后者的意思是简单的说,是否携带上身份信息。当然仅仅修改浏览器是不起作用的,还得对服务器端进行修改,代码如下。
<?php
// 1.php
header('Access-Control-Allow-Credentials:true');
header('Access-Control-Allow-Origin:'.getallheaders()['Origin']);
setcookie('aa', 'ad');
echo json_encode(['status' => 1, 'msg' => '成功'], JSON_UNESCAPED_UNICODE);
此处代码新增了Access-Control-Allow-Credentials一项,意为允许客户端携带身份凭证,值得注意的是,当使用Access-Control-Allow-Credentials时,Access-Control-Allow-Origin不能为*,可如以上代码设置为请求源。下面测试cookie信息是否已携带过来,新增2.php文件
<?php
// 2.php
header('Access-Control-Allow-Credentials:true');
header('Access-Control-Allow-Origin:'.getallheaders()['Origin']);
echo json_encode(['status' => 1, 'msg' => '成功', 'cookie' => $_COOKIE]);
先请求1.php,然后再请求2.php,从返回结果可以得知,cookie已成功设置且异步跨域请求已携带上cookie
完整代码如下:
<?php
// 1.php
header('Access-Control-Allow-Credentials:true');
header('Access-Control-Allow-Origin:'.getallheaders()['Origin']);
setcookie('aa', 'ad');
echo json_encode(['status' => 1, 'msg' => '成功'], JSON_UNESCAPED_UNICODE);
<?php
// 2.php
header('Access-Control-Allow-Credentials:true');
header('Access-Control-Allow-Origin:'.getallheaders()['Origin']);
echo json_encode(['status' => 1, 'msg' => '成功', 'cookie' => $_COOKIE]);
// 1.html
<button id="r2">request 2.php</button>
<script src="jquery-3.5.1.min.js"></script>
<script>
$.ajax({
url:'http://www.test.me/1.php',
crossDomain:true,
xhrFields:{
withCredentials: true
},
success:function(res){
console.log('res', res);
},
error:function(err_res) {
console.log('err_res', err_res)
}
})
$('#r2').click(function(){
$.ajax({
url:'http://www.test.me/2.php',
crossDomain:true,
xhrFields:{
withCredentials: true
},
success:function(res){
console.log('res', res);
},
error:function(err_res) {
console.log('err_res', err_res)
}
})
})
</script>
异常说明
如返回头部中的Set-Cookie项出现感叹号,是因为谷歌新版本升级后导致,解决办法为先后打开以下地址
chrome://flags/#same-site-by-default-cookies
chrome://flags/#cookies-without-same-site-must-be-secure
设置为Disabled,然后重启浏览器。