CORS请求Request携带Cookie失败占用License解决方案
起因
-
因为是前后端分离的项目,前端使用的
vue2
,后端iris
。需要获取cooike
的sessionid
,每次请求时携带cookie
,防止每次请求都占用一个license
。 -
登录认证,保持会话期间全局变量,超时退出。
现象
下图是如果不携带cookie
每次请求都会新建一个session
并且占用一个license
。
解决过程
所以基于上述情况,需要每次获取响应response
的cookie
,保存下来,下次请求request
再携带保存下来的cookie
。
如图Set-Cookie
内容:
CSPSESSIONID
- 为SessionID
的CSPSessionCookie
CSPWSERVERID
- 服务器ID
,负载均衡时会用到,如果ID
不对应,则会报错。
后端方法为%CSP.Response
的WriteHTTPHeaderCookies()
:
Method WriteHTTPHeaderCookies()
{
#Dim c,cookie,port,cookiepath
s ^yx("yx","UseSessionCookie") = %session.UseSessionCookie
s ^yx("yx","CSPSessionCookie") = %session.CSPSessionCookie
s ^yx("yx","SecureSessionCookie") = %session.SecureSessionCookie
If $isobject($get(%session)),%session.UseSessionCookie,i%OutputSessionToken,%session.CSPSessionCookie'="" {
Set cookie="CSPSESSIONID",cookiepath=i%CookiePath
If $extract(cookiepath,*)'="/" Set cookiepath=cookiepath_"/"
If $data(%request.CgiEnvs("SERVER_PORT"),port) Set cookie=cookie_"-SP-"_port
Set cookie=cookie_"-UP-"_$translate($extract(cookiepath,2,*),"/","-")
Set cookie=cookie_"="_$select(i%OutputSessionToken=2:"",1:%session.CSPSessionCookie)_"; path="_$zcvt($zcvt(cookiepath,"O","UTF8"),"O","URL")_"; "_$select(%session.SecureSessionCookie:" secure;",1:"")
if i%UseHttpOnly {
Set cookie=cookie_" httpOnly;"
}
Write "Set-Cookie: "_cookie,!
$$$SysLogTag(2,"CSPResponse","[WriteHTTPHeaderCookies] Session cookie: "_cookie,"", %request.RequestId)
s ^yx("Set-Cookie:") = cookie
Set %session.CookiePath=$get(cookiepath)
}
Set c=""
For {
Set c=$order(i%Cookies(c)) Quit:c=""
Set name=i%Cookies(c,"n")
Write "Set-Cookie: ",name,"=",i%Cookies(c,"v"),";"
Write:i%Cookies(c,"e")'="" " expires=",i%Cookies(c,"e"),";"
Write:i%Cookies(c,"p")'="" " path=",i%Cookies(c,"p"),";"
Write:i%Cookies(c,"d")'="" " domain=",i%Cookies(c,"d"),";"
Write:i%Cookies(c,"s") " secure;"
Write:i%Cookies(c,"h") " httpOnly;"
Write !
}
}
由于cookie
的httpOnly
属性导致js
无法获取cookie
去掉httpOnly
属性可以获取到但是又不安全。
后端设置httpOnly=0
默认为1
。
ClassMethod OnPreHTTP() As %Boolean
{
#dim %response as %CSP.Response
s %response.UseHttpOnly = 0
q 1
}
大多数XSS攻击都是针对会话cookie的盗窃。后端服务器可以通过在其创建的cookie上设置HttpOnly标志来帮助缓解此问题,这表明该cookie在客户端上不可访问。
之后修改axios
的验证方式如下2种:
- 每次想携带
cookie
的axios
请求,加上withCredentials:true
的属性.
this.$axios.get("", {
withCredentials:true,
params: {
ClassName: "IMP.Login.Api",
MethodName: "QueryServers"
}
}).then(res => {
}
- 修改
axios
默认配置, 使得每次请求默认携带cookie
.
// 自动验证session 防止占用lic
axios.defaults.withCredentials = true;
第一种方式应用于大部分请求不想带cookie
,只有少部分请求需要带上的情况,一般没有。第二种方式全局生效,所有的axios
请求都会带上cookie
,比较通用。
- 后端要把
Access-Control-Allow-Origin
的,"*"
替换为HTTP-ORIGN
。
#;d %response.SetHeader("Access-Control-Allow-Origin", "*")
d %response.SetHeader("Access-Control-Allow-Origin", ##class(IMP.Common.Data).GetOrigin())
/// desc: 获取源路径
/// w ##class(IMP.Common.Data).GetOrigin().%ToJSON()
ClassMethod GetOrigin()
{
q:'$d(%request) ""
q:'$d(%request.CgiEnvs("HTTP_ORIGIN")) ""
s origin = %request.CgiEnvs("HTTP_ORIGIN")
q origin
}
但是谷歌浏览器最新106
版本,每次请求还是不会复用session
。
chrome 80版本之后,谷歌把cookie的SameSite属性,从None改成了Lax。这时候,会导致cookie因为跨站而导致不会自动带上!
后端CORS
请求都是设置好的。
ClassMethod OnPreHTTP() As %Boolean
{
#dim %response as %CSP.Response
#; js是否可读cookie
s %response.UseHttpOnly = 0
#; SameSite=None,配合 Secure使用,https才好使
d %response.SetCookie("yx","yx; HttpOnly; SameSite=None")
/* 星号表示所有的域都可以接受 */
#;d %response.SetHeader("Access-Control-Allow-Origin", "*")
d %response.SetHeader("Access-Control-Allow-Origin", ##class(IMP.Common.Data).GetOrigin())
#; 允许请求方式,例如get。post
d %response.SetHeader("Access-Control-Allow-Methods", "*")
#; 允许头信息
d %response.SetHeader("Access-Control-Allow-Headers", "x-requested-with,content-type")
#; 允许验证
d %response.SetHeader("Access-Control-Allow-Credentials", "true")
#; 额外暴露头信息
d %response.SetHeader("Access-Control-Expose-Headers", "set-cookie")
d %response.SetHeader("Access-Control-Expose-Headers", "cookie")
#; 超时时间
d %response.SetHeader("Access-Control-Max-Age", 3600)
d %response.SetHeader("Set-Cookie", "HttpOnly; Secure=false; SameSite=None;")
/* 设置返回结构为Json */
d %response.SetHeader("Content-Type", "application/json")
q 1
}
查了一下资料发现是 Chrome 80
之后版本 对 SameSite
为空时的 默认值设置变更了。
以前
None
是默认值,但最近的浏览器版本将Lax
作为默认值,
以便对某些类型的跨站请求伪造 (CSRF
) 攻击具有相当强的防御能力。SameSite 属性有三个枚举值,分别是 strict/lax/none。Strict 最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。Lax 规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。
设置了 Strict 或 Lax 以后,基本就杜绝了 CSRF 攻击。当然,前提是用户浏览器支持 SameSite 属性。Chrome 计划将 Lax 变为默认设置。这时,网站可以选择显式关闭 SameSite 属性,将其设为 None。不过,前提是必须同时设置 Secure 属性(Cookie 只能通过 HTTPS 协议发送),否则无效。
网上解决方案:
谷歌浏览器地址栏输入:chrome://flags/
找到:SameSite by default cookies``、Cookies without SameSite must be secure
设置上面这两项设置成 Disable
或者在报文里面set-cookie
,添加SameSite=None;
Secure=true
,host
需要https
问题再一次出现,谷歌浏览器最新106版本没有此设置。无法更改。就此卡住。
之后想到用360浏览器,也是谷歌内核。发现携带cookie
成功。
但是对比谷歌浏览器失败。
之后修改谷歌浏览器配置后成功。
多次请求后端也不会出现的session
和占用lic
问题。
总结
- 设置前端
Vue
,axios
参数。 - 设置后端
IRIS
,Borker
。Access-Control-Allow-Origin
为请求源。 - 高版本谷歌浏览器
localhost
请求改为ipconfig
,IP
地址。
参考
axios response Set-Cookie 获取不到信息的情况,无法自动配置
关于 axios.defaults.withCredentials = true 不生效问题
[FE] Chrome 跨域请求失败:This Set-Cookie didn't specify a "SameSite" attribute, was defaulted to "SameS...