文章
· 十月 19, 2022 阅读大约需 7 分钟

CORS请求Request携带Cookie失败占用License解决方案

CORS请求Request携带Cookie失败占用License解决方案

起因

  1. 因为是前后端分离的项目,前端使用的vue2,后端iris。需要获取cooikesessionid,每次请求时携带cookie,防止每次请求都占用一个license

  2. 登录认证,保持会话期间全局变量,超时退出。

现象

下图是如果不携带cookie每次请求都会新建一个session并且占用一个license

image

image

解决过程

所以基于上述情况,需要每次获取响应responsecookie,保存下来,下次请求request再携带保存下来的cookie

image

如图Set-Cookie内容:

  • CSPSESSIONID - 为SessionIDCSPSessionCookie
  • CSPWSERVERID - 服务器ID,负载均衡时会用到,如果ID不对应,则会报错。

后端方法为%CSP.ResponseWriteHTTPHeaderCookies()

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 !
    }
}

由于cookiehttpOnly属性导致js无法获取cookie

image

去掉httpOnly属性可以获取到但是又不安全。

后端设置httpOnly=0 默认为1

ClassMethod OnPreHTTP() As %Boolean
{
    #dim %response as %CSP.Response
    s %response.UseHttpOnly = 0
    q 1
}

image

大多数XSS攻击都是针对会话cookie的盗窃。后端服务器可以通过在其创建的cookie上设置HttpOnly标志来帮助缓解此问题,这表明该cookie在客户端上不可访问。

之后修改axios的验证方式如下2种:

  1. 每次想携带cookieaxios请求,加上 withCredentials:true 的属性.
this.$axios.get("", {
        withCredentials:true,
        params: {
          ClassName: "IMP.Login.Api",
          MethodName: "QueryServers"
        }
      }).then(res => {

      }
  1. 修改axios默认配置, 使得每次请求默认携带cookie.
// 自动验证session 防止占用lic
axios.defaults.withCredentials = true;

第一种方式应用于大部分请求不想带cookie,只有少部分请求需要带上的情况,一般没有。第二种方式全局生效,所有的axios请求都会带上cookie,比较通用。

  1. 后端要把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
}

image

但是谷歌浏览器最新106版本,每次请求还是不会复用session

image

image

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

image

或者在报文里面set-cookie,添加SameSite=None; Secure=truehost需要https

问题再一次出现,谷歌浏览器最新106版本没有此设置。无法更改。就此卡住。

之后想到用360浏览器,也是谷歌内核。发现携带cookie成功。

image

image

但是对比谷歌浏览器失败。

image

之后修改谷歌浏览器配置后成功。

image

image

多次请求后端也不会出现的session和占用lic问题。

image

image

总结

  1. 设置前端Vueaxios参数。
  2. 设置后端IRISBorkerAccess-Control-Allow-Origin为请求源。
  3. 高版本谷歌浏览器localhost请求改为ipconfigIP地址。

参考

axios获取不到cookie

axios response Set-Cookie 获取不到信息的情况,无法自动配置

关于 axios.defaults.withCredentials = true 不生效问题

HttpOnly Cookie 怎么讲

SameSite是什么

[FE] Chrome 跨域请求失败:This Set-Cookie didn't specify a "SameSite" attribute, was defaulted to "SameS...

解决新版chrome跨域问题:cookie丢失以及samesite属性问题

Chrome浏览器改变SameSite设置

讨论 (0)1
登录或注册以继续