我们相信:世界是美好的,你是我也是。 来玩一下解压小游戏吧!

为啥说cookie级别的鉴权?因为鉴权方式很多,cookie级别是传统网站的接口鉴权方式,而现代构架里多使用header+token的方式进行数据传输处理。完成cookie级别的跨域接口鉴权,不是一件容易的事情,要满足的条件很多。在本文中,苏南大叔将要讲述跨域接口cookie鉴权,需要满足的一系列条件。

苏南大叔:Axios跨域请求接口,如何完成cookie级别的鉴权? - axios-cookie
Axios跨域请求接口,如何完成cookie级别的鉴权?(图2-1)

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。测试环境:win10chrome@135.0.7049.41nginx@1.15.11

动作发起方:axios 主动带 cookie

axios框架规定:ajax类似请求,默认是不携带cookie的。首先要先开启它的设定:withCredentials: true。其次确保请求的header符合服务器CORS策略(一般情况下都符合)。

参考方案:

axios.defaults.withCredentials = true;

或者:

const axiosInstance = axios.create({ withCredentials: true });

或者:

axios.get('<跨域接口>', {
    withCredentials: true,
    //...
})

参考文章:

动作接收方:cors 允许带 cookie

服务器端需要正确处理CORS请求,特别是OPTIONS预检请求:

  • 【重点】设置 Access-Control-Allow-Credentials:true,允许携带Cookie
  • 【重点】设置 Access-Control-Allow-Origin为允许的来源域名(如 https://newsn.net),这里可没说设置为*哦。
  • 设置 Access-Control-Allow-Methods,允许的HTTP方法(如GET,POST)。
  • 设置 Access-Control-Allow-Headers,允许的自定义头部(如Content-Type)。

参考文章:

服务器端颁发正确的cookie

因为是跨越的接口请求,所以要求:

  • 设置cookieSameSite属性为None,它要求secure属性。
  • 设置cookieSecure属性为True,它要求https协议。
  • 服务器端需使用https协议,它要求服务器部署证书。

参考文章:

客户端完整代码

苏南大叔:Axios跨域请求接口,如何完成cookie级别的鉴权? - 代码运行
Axios跨域请求接口,如何完成cookie级别的鉴权?(图2-2)

客户端代码:

<script src="https://fastly.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<button id="getRequest">Send GET Request</button>
<button id="postRequest">Send POST Request</button>
<div id="response"></div>
<script>
    const axiosInstance = axios.create({
        withCredentials: true, // 允许携带跨域Cookie
    });
    // 发送GET请求
    document.getElementById('getRequest').addEventListener('click', () => {
        axiosInstance.get('https://v/get.php?a=sunan')
            .then(response => {
                document.getElementById('response').innerText = JSON.stringify(response.data);
            })
            .catch(error => {
                document.getElementById('response').innerText = error;
            });
    });
    // 发送POST请求
    document.getElementById('postRequest').addEventListener('click', () => {
        axiosInstance.post('https://v/post.php', { a: 'sunan'},
            {headers: { 'Content-Type': 'application/x-www-form-urlencoded' }})
            .then(response => {
                document.getElementById('response').innerText = JSON.stringify(response.data);
            })
            .catch(error => {
                document.getElementById('response').innerText = 'POST Error: ' + error;
            });
    });
</script>

参考文章:

nginx配置

仅供参考:

server {
    listen       80;
    server_name  v;
    root   "E:/v";

    location / {
        include E:/v/nginx.htaccess;
        autoindex  off;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_split_path_info  ^((?U).+\.php)(/?.+)$;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        fastcgi_param  PATH_INFO  $fastcgi_path_info;
        fastcgi_param  PATH_TRANSLATED  $document_root$fastcgi_path_info;
        include        fastcgi_params;

        # 动态跨域处理
        set $cors_origin '';
        if ($http_origin ~* (http://h|http://newsn.net|https://newsn.net)) {
            set $cors_origin $http_origin;
        }

        # 添加跨域响应头
        add_header 'Access-Control-Allow-Origin' $cors_origin always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,access-control-allow-origin, authority, content-type, version-info, X-Requested-With' always;

        # 处理 OPTIONS 请求
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' $cors_origin always;
            add_header 'Access-Control-Allow-Credentials' 'true' always;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,access-control-allow-origin, authority, content-type, version-info, X-Requested-With' always;
            add_header 'Access-Control-Max-Age' 1728000 always;
            add_header 'Content-Length' 0;
            return 204;
        }
    }
}

服务端完整代码

最主要的就是使用https协议,然后设置默认cookiesamesitesecure。参考文章:

定制服务器端的cookie

需要对服务器端的session进行定制,主要就是securesamesite:
session_start.php:

function is_https() {
    return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') || 
           (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
}
session_name('MyCustomSession');
session_set_cookie_params([
    'lifetime' => 3600, // 1 小时
    'path' => '/',
    'domain' => $_SERVER['HTTP_HOST'], // 动态设置域名
    'secure' => is_https(), // 根据是否 HTTPS 设置 secure 属性
    'httponly' => true, // 防止 JavaScript 访问
    'samesite' => 'None' // 设置 SameSite 属性
]);
session_start();

其它代码

login.php:

require_once 'session_start.php';
$_SESSION["login"] = "ok";

check.php:

require_once 'session_start.php';
if(@$_SESSION["login"] != "ok"){
    echo "请先登录!";
    exit;
}

get.php

require_once 'check.php';
$aa = @$_GET["a"];
echo "{'ok':'$aa'}";

post.php

require_once 'check.php';
$aa = @$_POST["a"];
echo "{'ok':'$aa'}";

题外话

正常来说,鉴权很少用cookie了,都是在header头里面增加authorization字段,一般的鉴权方式都是Bearer。参考:

总结

1.Axios设置:withCredentials: true
2.服务器OPTIONS请求: 设置 Access-Control-Allow-OriginAccess-Control-Allow-Credentials等。
3.服务器端Cookie设置: 确保SameSite=NoneSecure=true
4.测试页面:使用 Axios发送跨域请求并携带Cookie

虽然本文没有提到csrf跨域脚本攻击,但是这就是目前的浏览器环境下,如果想要跨域攻击成功,所需要的基本条件。可见条件相当苛刻,很难达成。除非客户端是很古老版本的浏览器,或者被定制的浏览器。或者特殊版本的electron/nwjs,才有可能攻击成功。总之,突破口在客户端在浏览器本身之上,但是很难达成。

更多苏南大叔的axios相关经验文章,请参考:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   axios