OAuth及OIDC技术分享PPT杂记

OAuth及OIDC技术分享PPT杂记,第1张

目录
    • 对于认证的理解
      • JWT结构
    • OAuth2.0
      • 微信OAuth2.0登录
      • OAuth2.0 授权模式
        • Authorization Code Flow
        • Authorization Code Flow With PKCE
      • Client Credentials Flow
      • Resource Owner Password Flow
      • Device Authorization Flow
    • 即将到来的OAuth2.1
    • OIDC(OpenID Connect) 1.0
    • OAuth2.0 & OIDC 1.0 授权模式选型
    • SSO
    • SLO
    • Spring Security OAuth2 及 Auhorization Server
      • Security架构
      • Authorization Server核心FIlter
      • Spring Security OAuth2支持情况
      • 扩展后的Authorization Code + PKCE时序图
    • 认证架构演进
      • 前后端不分离 + RPC
      • 前后端分离
      • 前后端分离 + 应用网关
      • Istio对Resource Server的支持
      • APISIX对Resource Server的支持
    • 附录
      • 附录A - 微信OAuth端点
      • 附录B - OAuth & OIDC术语
      • 附录C - OAuth & OIDC常用端点

对于认证的理解
  • 传统session认证(认证通过信息写进session中、前后端不分离、模版引擎)
  • 分布式session管理(如借助redis统一管理session)
  • 单点登录、单点登出(需要统一的认证中心来维护用户认证信息,如CAS)
    • 在CAS中就已经使用了浏览器端重定向、颁发ticket、验证ticket并返回用户信息等,
    • 2类Session:CAS Server本身借助session来管理用户登录状态,而其他接入的Cas Client也借助各自的Session来管理各自的登录态。
    • 现阶段单点登录:多个SPA前端
  • OAuth三方登录集成(如对接微信登录)
    • OAuth也是利用浏览器重定向,code换accessToken,accessToken换用户信息的套路(类似CAS)
  • JWT的流行及无状态(使用JWT来代替session存储,契合OAuth2 Password)
    • 账号密码暴露给客户端
    • 客户端接入认证的逻辑总在变(如今天支持账号密码,明天支持手机号,后天支持微信登录,大后天…)
    • 吊销token的有状态
  • 身份云IDasS
    • https://www.authing.co/
    • https://auth0.com/
    • Azure ADFS


JWT结构

https://jwt.io/


OAuth2.0 微信OAuth2.0登录

https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html

几个角度:
1)微信:微信作为Authorization Server和Resource Server
2)身份云:身份云作为Authorization Server,我们的应用作为Client和Resource Server。
3)自建用户认证中心:我们自建Authorization Server,我们的应用作为Client和Resource Server。

起初对OAuth的理解就局限于借助第三方登录,并获取第三方用户信息,且转化为自己的系统用户,
当时看到比较多的就是OAuth2的4种授权类型,
但是随着学习的深入,发现OAuth2能做的远不止第三方登录。
之后再从OAuth2到OIDC(支持身份认证、单点登出等),貌似一套完整的统一认证模型就清晰了。

可以对比之前说的对接微信登录,那么:

  • 微信 - 是认证中心(提供了OAuth协议)
  • 微信 - 是资源(用户身份信息)的提供者
  • 我们的接入系统 - 是接入微信登录的客户端,且是资源(用户身份)的索取者

注:
通过Oauth 用户在微信平台的域名下完成认证,在微信平台的网页上输入用户认证信息,
接入微信登录的系统是无法获取到微信平台的用户账号、密码等信息的。

换个角度,假如把微信也变成我们自己内部的一个系统,即统一认证中心(管理用户及认证,实现Oauth协议),那么:

  • 我们的统一认证中心 - 提供了OAuth协议(用户管理、权限管理、认证UI管理等)
  • 我们的资源系统 - 是资源(如后端API接口)的提供者,需要对accessToken进行认证和鉴权
  • 我们的接入系统 - 是接入统一认证中心的客户端,且是资源(调用后端API)的索取者

从这个角度来看,我们自己也可以实现OAuth/OIDC协议,来作为我们自己的统一认证中心,
我们自己的内部应用都可以通过标准协议OAuth/OIDC进行接入,
将繁琐的用户认证工作都放到统一认证中心,减轻接入应用的负担,
而我们的资源服务仅需要对accessToken进行验证,
通过OAuth/OIDC还可以实现单点登录、单点登出,
而其他第三方应用也可以通过标准的OAuth/OIDC协议接入到我们的统一认证中心,
即通过我们的用户登录他们的系统。


OAuth2.0 授权模式
授权模式说明
授权码模式(Authorization Code)
通过code换取oken
较为常用,安全等级高,适用于存在Web后端的应用。
注:
通过code获取Token需要同时携带ClientId和ClientSecret,
所以该模式仅适用于可以安全存储ClientKey且不会泄露的应用(如后端服务),
而前端服务(SPA)、原生应用(桌面、移动端)都会暴露给具体用户,ClientKey有被用户拦截的风险,
下面提到的PKCE可适用于前端服务(SPA)、原生应用(桌面、移动端)。
授权码+PKCE模式(Authorization Code With PKCE)
通过code+code_verifier换取oken
可适用于前端服务(SPA)、原生应用(桌面、移动端)。
客户端凭证模式(Client Credentials)
通过Cient凭证(ClientId和Secret)换取token
适用于服务器间(M2M)通信。
刷新令牌模式(Refresh Token)
通过refresh_token换取新token
根据之前授权通过时颁发的refresh_token获取新的access_token,避免用户多次重复登录(无感登录),提升用户体验。
设备码模式(Device Code)
deviceCode+UserCode+VerficationUri
引导用户通过其他设备(如手机)进行认证授权
适用于可联网的无浏览器或输入受限的设备授权,例如智能电视、电子相册、打印机等。
隐式模式(Implicit)
直接返回token
不推荐,
作为授权码模式的备用选项。
密码模式(Password)
直接通过用户名密码换取token
不推荐,
最不安全,客户端需要知道用户账号,
且客户端与认证方式强绑定,后续认证方式修改客户端也需要一起修改。

Authorization Code Flow

注: 把上图中的第5、6、7步删除,即对应Implicit授权模式。


Authorization Code Flow With PKCE

PKCE(Proof Key for Code Exchange)

通过一种密码学手段阻止CSRF和Authorization Code注入攻击,
确保恶意第三方即使截获Authorization Code,也无法向认证服务器交换Access Token,
同时不需要在客户端存储ClientKey,避免ClientKey被泄露。
code_verifier 验证码,随机字符串
code_challenge 挑战码,code_challenge = Base64(Sha256(code_verifier))


Client Credentials Flow


Resource Owner Password Flow

不推荐
1)账号密码暴露给Client端,不安全
2)客户端与认证方式绑定,认证逻辑修改则所有Client都需要修改


Device Authorization Flow



即将到来的OAuth2.1

https://oauth.net/2.1/
https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-05

  • 仅保留Authorization Code, Client Credentials, Refresh Token, Device Code授权模式
  • 移除ImplicitPassword 授权模式
  • Authorization Code模式必须使用PKCE
  • Bearer Token不可使用query参数形式
  • Refresh Token for Public Client更严格(sender-contrained or one-time use)

OIDC(OpenID Connect) 1.0
  • OAuth2.0的超集
  • 身份层 - ID Token
  • UserInfo Endpoint
  • 单点登出

OIDC补充协议功能
OpenID Connect Core 1.0 - UserInfo EndpointRP端向OP发出查询用户详细信息的请求
OpenID Connect RP-Initiated Logout 1.0RP端向OP发出的初始登出请求(适用于单点登出SLO,end_session_endpoint),
OP首先清除OP端的session,
然后向当前session下的其他已登录的RP触发Front-Channel或者Back-Channel登出请求,
最终可通过post_logout_redirect_uri 重定向会当前RP
OpenID Connect Front-Channel Logout 1.0返回OP前端登出页面,
页面通过嵌入iframe(src=frontchannel_logout_uri),
同时触发当前OP Session对应的多个Client的登出接口
OpenID Connect Back-Channel Logout 1.0OP直接向当前OP Session对应的多个Client后端服务(不依赖User Agent,如不依赖浏览器)发送登出请求backchannel_logout_uri
且适用于User Agent被关闭的时候也可以退出登录。
OpenID Connect Session Management 1.0RP端通过check_session_iframe 监听当前UserAgent中用户的登录状态,
收到登录状态改变通知后RP应该清除自己的Session信息、页面跳转等。

OAuth2.0 & OIDC 1.0 授权模式选型


总结上图,关于OIDC的最优建议如下:

应用类型(即Client)选型建议
SPA
如Vue、React等单页应用(前后端分离),
有专门的后端资源服务层(Resource Server)提供API服务
PKCE + 授权码模式
即由前端接入认证,而后端仅接入鉴权
注: 如采用Nodej.js运行时环境并提供安全的后端存储,则可直接使用授权码模式。
原生应用Native(移动端应用、桌面应用)
如Android、Ios(前后端分离),
有专门的后端资源服务层(Resource Server)提供API服务
PKCE + 授权码模式
即由前端接入认证,而后端仅接入鉴权
WEB应用(即存在Web后端服务)
适用于获取第三方资源、集成第三方登录(如微信登录)、集成SSO
授权码模式
接入Oauth后携带code回调到Web后端,
由后端获取AccessToken,
在后端携带AccessToken请求资源,
可使用session存储Token和User信息,
即使用session作为当前Web应用的身份验证机制
服务器间通信
无终端用户
Client Credentials 模式

SSO


SLO


Spring Security OAuth2 及 Auhorization Server

Spring Seurity在2019年11月发布了OAuth 2.0 Migration Guide,

  • 宣布禁用原spring-security-oauth,并将其合并到Spring Security 5.2+,
  • 并且不再提供Authorization Server的支持。

后续在广大开发者的呼声下,在2020年4月Spring官方发布声明Announcing the Spring Authorization Server,

  • 宣布启动由社区主导的spring-authorization-server项目,专注于OAuth2 Authorization Server实现,
  • 目前最新版本为spring-authorization-server:0.2.2(发布于2022-01-27)。

截止到今天(2022-04-21):

  • Spring Security 5.6.3- OAuth2
    • OAuth2 Log In
    • OAuth2 Client
    • OAuth2 Resource Server
  • Spring社区版 - OAuth2 Authorization Server 0.2.3

如上几大模块组成了Spring Security对OAuth2生态的最新支持。


Security架构


比如认证相关的Filter,其总体结构如下:

  • AuthenticationProcessingFilter - 认证过滤器
    • AuthenticationManager -> ProviderManager - 认证管理器
      • AuthenticationProvider - 具体的认证逻辑实现
    • AuthenticationSuccessHandler - 认证成功处理器
    • AuthenticationFailureHandler - 认证失败处理器

Authorization Server整体调用层次:

  • EndpointFilter
    • AuthenticationConverter - 转换request为Authentication
    • AuthenticationManager -> ProviderManager - 认证管理器
      • AuthenticationProvider.authenticate(authentication)
    • AuthenticaionSuceessHandler.onAuthenticationSuccess
    • AuthenticaionFailureHandler.onAuthenticationFailure

Authorization Server核心FIlter
核心FIlterendpoint说明
OAuth2AuthorizationEndpointFilterGET|POST /oauth2/authorize授权端点,即RP跳转到OP的认证入口,
且EU认证通过后,OP重定向回RP,且附加code参数
OAuth2ClientAuthenticationFilterPOST /oauth2/token|introspect|revoke即RP向OP发送获取token请求、检查token、吊销token时,OP端提供的认证逻辑
OAuth2TokenEndpointFilterPOST /oauth2/tokenToken端点,RP向OP请求Token(通过code换token、执行refresh_token流程)
OAuth2TokenIntrospectionEndpointFilterPOST /oauth2/introspect校验Token端点,RP请求OP检测token有效性
OAuth2TokenRevocationEndpointFilterPOST /oauth2/revoke吊销Token端点,RP请求OP吊销token
OidcProviderConfigurationEndpointFilterGET /.well-known/openid-configurationOIDC协议发现端点
OidcUserInfoEndpointFilterGET /userinfo用户信息端点,提供用户信息查询
OidcClientRegistrationEndpointFilterPOST /connect/register客户端信息注册端点(暂未使用)

Spring Security OAuth2支持情况

查看Spring Security相关源码,可以发现如上提到的核心功能支持情况如下表:

功能Authorization ServerOAuth2 Client
Authorization Code Flow with PKCE
授权码Code流程 + PKCE
✔️✔️
Refresh Token without client_secret
PKCE后续无client_secret刷新token
Refresh Token Retotation(Reuse)
令牌轮换
✔️✔️
OAuth2 SSO
单点登录
✔️✔️
OIDC end_session_endpoint
单点登出
✔️

所以以上表格中未实现的,也就是需要扩展的功能。


扩展后的Authorization Code + PKCE时序图


认证架构演进 前后端不分离 + RPC

前后端分离

前后端分离 + 应用网关

Istio对Resource Server的支持

RequestAuthentication 整体配置结构如下:

  • namespace 策略生效的命名空间
  • selector -> matchLables 指定策略作用的目标workload
  • jwtRules jwt验证规则的配置
    • issuer jwt的发布者
    • audiences 受众,即jwt的接受者
    • jwksUri | jwks jwks(公钥),即用来验证jwt签名的公钥,jwksUri和jwks二选一
    • fromHeaders jwt在请求头的位置
      • name header名称
      • prefix token前缀,如"Bearer "
    • fromParams jwt在query参数中的参数名称
    • outputPayloadToHeader 指定jwt验证通过后的payload传递给哪个请求header
    • forwardOriginalToken 是否保留原始token,默认false

jwtRules概括起来分为如下几类配置:

  • jwt token的位置(jwtRules.fromHeaders, jwtRules.fromParams)
  • jwt.payload等相关属性(jwtRules.issuer, jwtRules.audiences)、request
  • jwt公钥,即JWKS(JSON Web Key Set),用来验证jwt的签名(jwtRules.jwksUri, jwtRules.jwks)
  • 是否保留jwt(jwtRules.outputPayloadToHeader, jwtRules.forwardOriginalToken)

RequestAuthentication 配置示例

apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
  name: httpbin
  namespace: foo
spec:
  # worload的label选择器
  selector:
    matchLabels:
      app: httpbin
  # jwt验证规则
  jwtRules:
    # jwt的发布者
  - issuer: "issuer-foo"
    # 受众,即jwt的接受者
    audiences:
	- bookstore_android.apps.example.com
  	  bookstore_web.apps.example.com
    # jwks(公钥)uri发现地址,即用来验证jwt签名的公钥
    jwksUri: https://example.com/.well-known/jwks.json
    # jwks(此属性与jwksUri需二选一)
    jwks: "jwks json"
  	# jwt在请求头的位置
  	fromHeaders:
  	- name: x-jwt-assertion
  	  prefix: "Bearer "
    # jwt在query参数中的参数名称
    fromParams:
    - "my_token"
    # 指定jwt验证通过后的payload传递给哪个请求header
    # 传递格式:base64_encoded(jwt_payload_in_JSON)
    outputPayloadToHeader: payload_header
    # 是否保留原始token(若保留则继续传递token到upstream请求),默认false
    forwardOriginalToken: false

注:

  • jwt验证通过,则正常接受请求
  • jwt验证失败,则拒绝请求
  • jwt为空,默认接受请求(需通过设置Authoriation policies来拒绝不带token的请求)
  • 支持多个不同位置的jwt token,
  • 但仅支持一个有效的token(多个有效的token会导致输出的principal不确定)

AuthorizationPolicy 其整体结构如下:

  • namespace 策略生效的命名空间(即目标workload所属的namespace)
  • selector -> matchLables 指定策略作用的目标workload
  • actions: CUSTOM | DENY | ALLOW 是否允许请求
  • rules 指定触发action的条件(即满足rules定义的请求,则执行action)
    • from -> source[] 指定请求来源(source满足的条件),为空则表示全部来源
      pincipals(workload身份)、requestPrincipals(用户身份)、namespaces、ipBlocks、remoteIpBlocks及相应not属性
    • to -> operation[] 指定请求的 *** 作(request满足的条件),为空则表示所有 *** 作
      hosts、ports、methods、paths及相应not属性
    • when 指定额外的附件条件(key对应Istio属性)
      • key -> values[] | notValues[]
        request.headers[header_name]、
        request.auth.[principal | audiences | presenter | claims[claimName] ]、
        source.[ip | namespace | principal ]、remote.ip、
        destination.[ip | port]、 connection.sni

AuthorizationPolicy配置概括起来就是:
允许(或拒绝) 来源workload 或 用户目标workload进行什么 *** 作,并且需要满足什么条件

具体配置示例:

# 作用目标:               workload(namespace=foo, label(app=httpbin, version=v1))
# 动作action:            允许访问ALLOW
# 来源rules.from.source: principals=="cluster.local/ns/default/sa/sleep" or namepsace=="dev"
#  *** 作rules.to.operation:method=="GET"
# 条件rules.when:        request.auth.claims[iss]=="https://accounts.google.com"
# 即[仅允许][cluster.local/ns/default/sa/sleep身份(PeerAuthentication mTls验证通过)、或者dev命名空间]的workload访问[httpbin:v1服务的GET请求]
# 且[用户RequestAuthentication JWT验证通过、且jwt.claims[iss]=="https://accounts.google.com"]
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
 name: httpbin
 namespace: foo
spec:
 selector:
   matchLabels:
     app: httpbin
     version: v1
 action: ALLOW
 rules:
 - from:
   - source:
   	   # 指定来源workload身份
   	   # principals: ["*"]则表示需要PeerAuthentication mtls验证通过
       principals: ["cluster.local/ns/default/sa/sleep"]
   - source:
       # 指定来源namespace
       namespaces: ["dev"]
   to:
   - operation:
       # 执行GET请求
       methods: ["GET"]
   when:
     # 指定用户jwt需验证通过,且iss为指定值
   - key: request.auth.claims[iss]
     values: ["https://accounts.google.com"]


# [拒绝][不属于foo命名空间的服务]访问[httpbin:v1服务]
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
 name: httpbin-deny
 namespace: foo
spec:
 selector:
   matchLabels:
     app: httpbin
     version: v1
 action: DENY
 rules:
 - from:
   - source:
       # 不是来自于foo命名空间
       notNamespaces: ["foo"]

# 访问非/healthz的请求,均需要JWT验证通过,
# 而访问/healthz的请求,可无需JWT验证(即请求未携带JWT令牌、或者携带的JWT令牌为空)
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: disable-jwt-for-healthz
  namespace: default
spec:
  selector:
    matchLabels:
      app: products
  action: ALLOW
  rules:
  - to:
    - operation:
        # 访问除了/healthz以外的path
        notPaths: ["/healthz"]
    from:
    - source:
        # 需要RequestAuthentication JWT验证通过
        requestPrincipals: ["*"]
APISIX对Resource Server的支持


附录
附录A - 微信OAuth端点

并非标准OAuth2 Authorization Code模式,例如:

  • appid, secret
  • token_endpoint不唯一
  • https://open.weixin.qq.com/connect/qrconnect
    • ?appid=APPID
    • &redirect_uri=REDIRECT_URI
    • &response_type=code
    • &scope=snsapi_login|snsapi_userinfo
    • &state=STATE
    • #wechat_redirect
  • https://api.weixin.qq.com/sns/oauth2/access_token
    • ?appid=APPID
    • &secret=SECRET
    • &code=CODE
    • &grant_type=authorization_code
  • https://api.weixin.qq.com/sns/oauth2/refresh_token
    • ?appid=APPID
    • &grant_type=refresh_token
    • &refresh_token=REFRESH_TOKEN
  • https://api.weixin.qq.com/sns/userinfo
    • ?access_token=ACCESS_TOKEN
    • &openid=OPENID

附录B - OAuth & OIDC术语

OIDC及OAuth核心术语:

OIDCOAuth说明
EU
End User
Resource Owner拥有资源的用户
RP
Relying Party
Client
Third-party application
用来代指OAuth2中的受信任的客户端(Web应用、SPA、移动端应用…),
即接入认证中心(OP、Authorization Server)登录跳转且并且获取Token(IdToken、AccessToken),
可通过IdToken获取用户身份信息,
然后携带AccessToken访问资源(Resource Server、Userinfo Endpoint)
OP
OpenID Provider
Auhtorization Server为用户(EU、Resource Owner)提供认证的服务(如微信授权平台、Github授权平台、自建用户中心等),
用来为应用客户端(RP、Client)提供用户(EU、Resource Owner)的身份认证信息
Resource ServerResource Server资源服务器,
即提供资源API且需要对(受保护的)资源访问进行鉴权的服务,
Cilent端携带AccessToken访问ResourceServer,
然后由Resource Server对此AccessToken进行鉴权
EndpointEndpoint端点,即提供的API接口
ID Token身份令牌(JWT),
用于认证,标识用户身份已经认证,可用于获取用户身份信息(如用户名、头像等)。
Access TokenAccess Token访问令牌(JWT),
用于鉴权,适用于API的访问鉴权。
User AgentUser Agent应用执行端,
如浏览器,手机端,即RP的执行端

附录C - OAuth & OIDC常用端点

OIDC和OAuth的常用端点(Endpoint,即提供的API接口)

endpoint提供方调用方认证格式desc
authoriztion_endpointOPRPCode Flow模式:
GET authorization_endpoint
?response_type=code
&redirect_uri={redirect_uri}
&client_id={client_id}
&state={random_str}
&nonce={another_random_str}
&scope=openid profile email

PKCE + Code Flow模式
(额外添加code_challenge参数)

GET authorization_endpoint
?response_type=code
&redirect_uri={redirect_uri}
&client_id={client_id}
&state={random_str}
&nonce={another_random_str}
&scope=openid profile email
&code_challenge_method=S256
&code_challenge={code_challenge}
进入OP登录授权界面
redirect_uriRPOPGET redirect_uri
?code={code}
&state={random_str}
在OP登录成功后,将code回调给RP
token_endpointOPRPclient_secret_post
client_secret_basic
Code Flow模式:
POST token_endpoint
?grant_type=authorization_code
&code={code}
&redirect_uri={redirect_uri}
&client_id={client_id}
&client_secret={client_secret}

PKCE + Code Flow模式
(删除client_secret参数后 额外添加code_verfier参数)

POST token_endpoint
?grant_type=authorization_code
&code={code}
&redirect_uri={redirect_uri}
&client_id={client_id}
&code_verifier={code_verifier}

Refresh Token模式:
POST token_endpoint
?grant_type=refresh_token
&client_id={client_id}
&client_secret={client_secret}
&refresh_token={refresh_token}
注:在PKCE模式下支持无client_secret执行刷新token流程
通过code获取access_token、id_token,

根据refresh_token获取新的access_token
userinfo_endpointOPRPBearer {access_token}GET userinfo_endpoint根据acces_token获取用户信息
introspection_endpointOPResource Serverclient_secret_post
client_secret_basic
POST introspection_endpoint
?token={your_token}
用于验证access_token是否有效,
通过返回结果中的active进行标识
revocation_endpointOPRPclient_secret_post
client_secret_basic
POST revocation_endpoint
?token={your_token}
&token_type_hint={}
&token_type_hint=access_token
撤销token、access_token,
若撤销refresh_token,则此refresh_token关联的access_token也会被撤销
end_session_endpointOPRPGET end_session_point
?id_token_hint={id_token_issued_to_client}
&post_logout_redirect_uri={post_logout_redirect_uri}
&state={random_str}
结束浏览器中OP的session会话,
并触发后续的多RP的Front-Channel或Back-Channel登出,
最后可配合post_logout_redirect_uri 重定向回RP页面
post_logout_redirect_uriRPOPGET post_logout_redirect_uriOP登出成功后会重定向回RP页面,
或对应于RP登录界面、登出状态展示页面等
check_session_iframeOPRPRP iframe.src=check_session_iframeSession Management,检查当前RP的登录状态
frontchannel_logout_uriRPOP iframeGET fontchannel_logout_uri
?iss={issureId}
&sid={idToken.sid}
RP前端实现的登出处理页面,
由OP Front-Channel模式下返回的Html iframe调用
backchannel_logout_uriRPOPPOST backchannel_logout_uri
?logout_token={logout_token like idToken}
RP后端实现的后端登出接口,
RP后端需验证logout_token并根据logout_token找出并清除后端的对应的session信息

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/720885.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-25
下一篇 2022-04-25

发表评论

登录后才能评论

评论列表(0条)

保存