技术饭
解决两个跨域但是同站、同域、同源的域名(Sec-Fetch-Site: same-site),通过接口请求设置session_id()改变PHPSESSID的问题
工具:
前端域名:htps://qd.copylian.com
接口域名:https://api.copylian.com
验证码接口:https://api.copylian.com/index.php/get/verify
现象:
1、两个https的二级域名解析在同一台服务器上,端口都是443
2、前端域名与接口域名同时在同一个浏览器打开,接口域名登录后台生成PHPSESSID,保存了登录状态
3、前端域名请求接口域名生成验证码,验证保存在session里面,由于访问接口,每次的会话session_id都是不一致,为了实现跨域验证码的验证,这个时候需要在接口端设置session_id(随机数),然后把这个随机值返回给前端,到时候验证的时候再设置session_id(随机数),这样会话才能保持一致,验证码才能通过验证
3-1、重新生成session_id
3-2、验证验证码设置session_id
4、由于步骤3设置session_id(随机数)重新生成PHPSESSID后,导致了步骤2登录后台的PHPSESSID被改成了步骤3设置的PHPSESSID,这时候步骤2的登录状态就消失了,直接退出登录
4-1、前端接口返回一个接口域名根目录的PHPSESSID
4-2、接口域名的PHPSESSID也被改变了
分析:
1、经过多轮测试,发现如果把其中一个https改为http那么请求属性 Sec-Fetch-Site 就变成了 cross-site,这样是没问题的,也就是跨域但不同域、不同站、不同源的话PHPSESSID是没问题的,但是我们的需求必须都是https
2、前端设置请求头 Sec-Fetch-Site:cross-site,但是查看了 SameSite 的介绍以及解析,发现Sec-开头的是被保护的无法直接设置
3、网上很多资料说是通过后端头或者配置修改 SameSite 的值,但是php7.2.24暂时没有SameSite配置,php7.3以上版本才有此项配置,网上给出的php7.2版本及一下的配置方法是:header('Set-Cookie: cross-site-cookie=name; SameSite=None; Secure'); 但是配置之后还是无效果
4、尝试把接口端域名生成的PHPSESSID保存到redis里面,然后再取出来重新设置这个PHPSESSID值,这样设置似乎看起来没问题,但是每个浏览器无法确定唯一性,所以也作罢
5、最后发现:既然PHPSESSID能被跨域请求修改,但是跨域请求有获取不到PHPSESSID的值,而且PHPSESSID的cookie存的path是根目录 / 下,当时猜想如果我重新生成的PHPSESSID把存储目录做修改是否就能解决问题呢!果然!答案是肯定的,直接将session的path目录改成 /tmp,当然这个目录必须存在切可写,不过这也是一个折中的办法,暂时我也没找到更好的法子
参考资料:
文明上网理性发言!