使用 jwt 完成 sso 单点登录

本贴最后更新于 2271 天前,其中的信息可能已经时异事殊

来源

https://bestqliang.com/2018/06/02/%E4%BD%BF%E7%94%A8jwt%E5%AE%8C%E6%88%90sso%E5%8D%95%E7%82%B9%E7%99%BB%E5%BD%95/

JWT

在了解 jwt 之前,先了解一下常用的会话管理

  • 基于 server-session 的管理方式
  • cookie-based 的管理方式
  • token-based 的管理方式

一.基于 server-session 的管理

  1. 服务端 session 是用户第一次访问应用时,服务器就会创建的对象,代表用户的一次会话过程,服务器为每一个 session 都分配一个唯一的 sessionid,以保证每个用户都有一个不同的 session 对象。
  2. 服务器在创建完 session 后,会把 sessionid 通过 cookie 返回给用户所在的浏览器,这样当用户第二次及以后向服务器发送请求的时候,就会通过 cookiesessionid 传回给服务器,以便服务器能够根据 sessionid 找到与该用户对应的 session 对象。
  3. session 通常有失效时间的设定,比如 2 个小时。当失效时间到,服务器会销毁之前的 session,并创建新的 session 返回给用户。但是只要用户在失效时间内,有发送新的请求给服务器,通常服务器都会把他对应的 session 的失效时间根据当前的请求时间再延长 2 个小时。
  4. session 在一开始并不具备会话管理的作用。它只有在用户登录认证成功之后,并且往 session 对象里面放入了用户登录成功的凭证,才能用来管理会话。管理会话的逻辑也很简单,只要拿到用户的 session 对象,看它里面有没有登录成功的凭证,就能判断这个用户是否已经登录。当用户主动退出的时候,会把它的 session 对象里的登录凭证清掉。所以在用户登录前或退出后或者 session 对象失效时,肯定都是拿不到需要的登录凭证的。

以上过程可简单使用流程图描述如下:

它还有一个比较大的优点就是安全性好,因为在浏览器端与服务器端保持会话状态的媒介始终只是一个 sessionid 串,只要这个串够随机,攻击者就不能轻易冒充他人的 sessionid 进行操作;除非通过 CSRF 或 http 劫持的方式,才有可能冒充别人进行操作;即使冒充成功,也必须被冒充的用户 session 里面包含有效的登录凭证才行。但是在真正决定用它管理会话之前,也得根据自己的应用情况考虑以下几个问题:

  1. 这种方式将会话信息存储在 web 服务器里面,所以在用户同时在线量比较多时,这些会话信息会占据比较多的内存;
  2. 当应用采用集群部署的时候,会遇到多台 web 服务器之间如何做 session 共享的问题。因为 session 是由单个服务器创建的,但是处理用户请求的服务器不一定是那个创建 session 的服务器,这样他就拿不到之前已经放入到 session 中的登录凭证之类的信息了;
  3. 多个应用要共享 session 时,除了以上问题,还会遇到跨域问题,因为不同的应用可能部署的主机不一样,需要在各个应用做好 cookie 跨域的处理。

针对问题 1 和问题 2,我见过的解决方案是采用 redis 这种中间服务器来管理 session 的增删改查,一来减轻 web 服务器的负担,二来解决不同 web 服务器共享 session 的问题。针对问题 3,由于服务端的 session 依赖 cookie 来传递 sessionid,所以在实际项目中,只要解决各个项目里面如何实现 sessionidcookie 跨域访问即可,这个是可以实现的,就是比较麻烦,前后端有可能都要做处理。

二. cookie-based 的管理方式

由于前一种方式会增加服务器的负担和架构的复杂性,所以后来就有人想出直接把用户的登录凭证直接存到客户端的方案,当用户登录成功之后,把登录凭证写到 cookie 里面,并给 cookie 设置有效期,后续请求直接验证存有登录凭证的 cookie 是否存在以及凭证是否有效,即可判断用户的登录状态。使用它来实现会话管理的整体流程如下:

  1. 用户发起登录请求,服务端根据传入的用户密码之类的身份信息,验证用户是否满足登录条件,如果满足,就根据用户信息创建一个登录凭证,这个登录凭证简单来说就是一个对象,最简单的形式可以只包含用户 id,凭证创建时间和过期时间三个值。
  2. 服务端把上一步创建好的登录凭证,先对它做数字签名,然后再用对称加密算法做加密处理,将签名、加密后的字串,写入 cookiecookie 的名字必须固定(如 ticket),因为后面再获取的时候,还得根据这个名字来获取 cookie 值。这一步添加数字签名的目的是防止登录凭证里的信息被篡改,因为一旦信息被篡改,那么下一步做签名验证的时候肯定会失败。做加密的目的,是防止 cookie 被别人截取的时候,无法轻易读到其中的用户信息。
  3. 用户登录后发起后续请求,服务端根据上一步存登录凭证的 cookie 名字,获取到相关的 cookie 值。然后先做解密处理,再做数字签名的认证,如果这两步都失败,说明这个登录凭证非法;如果这两步成功,接着就可以拿到原始存入的登录凭证了。然后用这个凭证的过期时间和当前时间做对比,判断凭证是否过期,如果过期,就需要用户再重新登录;如果未过期,则允许请求继续。

这种方式最大的优点就是实现了服务端的无状态化,彻底移除了服务端对会话的管理的逻辑,服务端只需要负责创建和验证登录 cookie 即可,无需保持用户的状态信息。对于第一种方式的第二个问题,用户会话信息共享的问题,它也能很好解决:因为如果只是同一个应用做集群部署,由于验证登录凭证的代码都是一样的,所以不管是哪个服务器处理用户请求,总能拿到 cookie 中的登录凭证来进行验证;如果是不同的应用,只要每个应用都包含相同的登录逻辑,那么他们也是能轻易实现会话共享的,不过这种情况下,登录逻辑里面数字签名以及加密解密要用到的密钥文件或者密钥串,需要在不同的应用里面共享,总而言之,就是需要算法完全保持一致。

这种方式由于把登录凭证直接存放客户端,并且需要 cookie 传来传去,所以它的缺点也比较明显:

  1. cookie 有大小限制,存储不了太多数据,所以要是登录凭证存的消息过多,导致加密签名后的串太长,就会引发别的问题,比如其它业务场景需要 cookie 的时候,就有可能没那么多空间可用了;所以用的时候得谨慎,得观察实际的登录 cookie 的大小;比如太长,就要考虑是非是数字签名的算法太严格,导致签名后的串太长,那就适当调整签名逻辑;比如如果一开始用 4096 位的 RSA 算法做数字签名,可以考虑换成 1024、2048 位;
  2. 每次传送 cookie,增加了请求的数量,对访问性能也有影响;
  3. 也有跨域问题,毕竟还是要用 cookie

前面两种会话管理方式因为都用到 cookie,不适合用在 native app 里面:native app 不好管理 cookie,毕竟它不是浏览器。这两种方案都不适合用来做纯 api 服务的登录认证。要实现 api 服务的登录认证,就要考虑下面要介绍的第三种会话管理方式。

三.token-based 的管理方式

这种方式从流程和实现上来说,跟 cookie-based 的方式没有太多区别,只不过 cookie-based 里面写到 cookie 里面的 ticket 在这种方式下称为 token,这个 token 在返回给客户端之后,后续请求都必须通过 url 参数或者是 http header 的形式,主动带上 token,这样服务端接收到请求之后就能直接从 http header 或者 url 里面取到 token 进行验证:

这种方式不通过 cookie 进行 token 的传递,而是每次请求的时候,主动把 token 加到 http header 里面或者 url 后面,所以即使在 native app 里面也能使用它来调用我们通过 web 发布的 api 接口。app 里面还要做两件事情:

  1. 有效存储 token,得保证每次调接口的时候都能从同一个位置拿到同一个 token
  2. 每次调接口的的代码里都得把 token 加到 header 或者接口地址里面。

看起来麻烦,其实也不麻烦,这两件事情,对于 app 来说,很容易做到,只要对接口调用的模块稍加封装即可。

这种方式同样适用于网页应用,token 可以存于 localStorage 或者 sessionStorage 里面,然后每发 ajax 请求的时候,都把 token 拿出来放到 ajax 请求的 header 里即可。不过如果是非接口的请求,比如直接通过点击链接请求一个页面这种,是无法自动带上 token 的。所以这种方式也仅限于走纯接口的 web 应用。

这种方式用在 web 应用里也有跨域的问题,比如应用如果部署在 a.com,api 服务部署在 b.com,从 a.com 里面发出 ajax 请求到 b.com,默认情况下是会报跨域错误的,这种问题可以用 CORS(跨域资源共享)的方式来快速解决。

这种方式跟 cookie-based 的方式同样都还有的一个问题就是 ticket 或者 token 刷新的问题。有的产品里面,你肯定不希望用户登录后,操作了半个小时,结果 ticket 或者 token 到了过期时间,然后用户又得去重新登录的情况出现。这个时候就得考虑 tickettoken 的自动刷新的问题,简单来说,可以在验证 tickettoken 有效之后,自动把 tickettoken 的失效时间延长,然后把它再返回给客户端;客户端如果检测到服务器有返回新的 tickettoken,就替换原来的 tickettoken

四. 安全问题

在 web 应用里面,会话管理的安全性始终是最重要的安全问题,这个对用户的影响极大。

首先从会话管理凭证来说,第一种方式的会话凭证仅仅是一个 sessionid,所以只要这个 sessionid 足够随机,而不是一个自增的数字 id 值,那么其它人就不可能轻易地冒充别人的 sessionid 进行操作;第二种方式的凭证 ticket 以及第三种方式的凭证 token 都是一个在服务端做了数字签名,和加密处理的串,所以只要密钥不泄露,别人也无法轻易地拿到这个串中的有效信息并对它进行篡改。总之,这三种会话管理方式的凭证本身是比较安全的。

然后从客户端和服务端的 http 过程来说,当别人截获到客户端请求中的会话凭证,就能拿这个凭证冒充原用户,做一些非法操作,而服务器也认不出来。这种安全问题,可以简单采用 https 来解决,虽然可能还有 http 劫持这种更高程度的威胁存在,但是我们从代码能做的防范,确实也就是这个层次了。


JWT 介绍 (https://jwt.io/)

JSON Web Token(JWT) 是一个开放标准(RFC 7519),它定义了一种紧凑和自包含的方式,用于在各方之间作为 JSON 对象安全地传输信息。作为标准,它没有提供技术实现,但是大部分的语言平台都有按照它规定的内容提供了自己的技术实现,所以实际在用的时候,只要根据自己当前项目的技术平台,到官网上选用合适的实现库即可。

使用 JWT 来传输数据,实际上传输的是一个字符串,这个字符串就是所谓的 json web token 字符串。所以广义上,JWT 是一个标准的名称;狭义上,JWT 指的就是用来传递的那个 token 字符串。这个串有两个特点:

  1. 紧凑:指的是这个串很小,能通过 url 参数,http 请求提交的数据以及 http header 的方式来传递;
  2. 自包含:这个串可以包含很多信息,比如用户的 id、角色等,别人拿到这个串,就能拿到这些关键的业务信息,从而避免再通过数据库查询等方式才能得到它们。

通常一个 JWT 是长这个样子的:

要知道一个 JWT 是怎么产生以及如何用于会话管理,只要弄清楚 JWT 的数据结构以及它签发和验证的过程即可。

一. JWT 的数据结构以及签发过程

一个 JWT 实际上是由三个部分组成:header(头部)payload(载荷)signature(签名)。这三个部分在 JWT 里面分别对应英文句号分隔出来的三个串:

先来看 header 部分的结构以及它的生成方法。header 部分是由下面格式的 json 结构生成出来:

这个 json 中的 typ 属性,用来标识整个 token 字符串是一个 JWT 字符串;它的 alg 属性,用来说明这个 JWT 签发的时候所使用的签名和摘要算法,常用的值以及对应的算法如下:

typalg 属性的全称其实是 typealgorithm,分别是类型跟算法的意思。之所以都用三个字母来表示,也是基于 JWT 最终字串大小的考虑,同时也是跟 JWT 这个名称保持一致,这样就都是三个字符了…typalgJWT 中标准中规定的属性名称,虽然在签发 JWT 的时候,也可以把这两个名称换掉,但是如果随意更换了这个名称,就有可能在 JWT 验证的时候碰到问题,因为拿到 JWT 的人,默认会根据 typalg 去拿 JWT 中的 header 信息,当你改了名称之后,显然别人是拿不到 header 信息的,他又不知道你把这两个名字换成了什么。JWT 作为标准的意义在于统一各方对同一个事情的处理方式,各个使用方都按它约定好的格式和方法来签发和验证 token,这样即使运行的平台不一样,也能够保证 token 进行正确的传递。

一般签发 JWT 的时候,header 对应的 json 结构只需要 typalg 属性就够了。JWTheader 部分是把前面的 json 结构,经过 Base64Url 编码之后生成出来的:

(在线 base64 编码:http://www1.tc711.com/tool/BASE64.htm)

再来看 payload 部分的结构和生成过程。payload 部分是由下面类似格式的 json 结构生成出来:

payload 的 json 结构并不像 header 那么简单,payload 用来承载要传递的数据,它的 json 结构实际上是对 JWT 要传递的数据的一组声明,这些声明被 JWT 标准称为 claims,它的一个“属性值对”其实就是一个 claim,每一个 claim 的都代表特定的含义和作用。比如上面结构中的 sub 代表这个 token 的所有人,存储的是所有人的 IDname 表示这个所有人的名字;admin 表示所有人是否管理员的角色。当后面对 JWT 进行验证的时候,这些 claim 都能发挥特定的作用。

根据 JWT 的标准,这些 claims 可以分为以下三种类型:

  1. Reserved claims(保留),它的含义就像是编程语言的保留字一样,属于 JWT 标准里面规定的一些 claimJWT 标准里面定好的 claim 有:

iss(Issuser):代表这个 JWT 的签发主体;
sub(Subject):代表这个 JWT 的主体,即它的所有人;
aud(Audience):代表这个 JWT 的接收对象;
exp(Expiration time):是一个时间戳,代表这个 JWT 的过期时间;
nbf(Not Before):是一个时间戳,代表这个 JWT 生效的开始时间,意味着在这个时间之前验证 JWT 是会失败的;
iat(Issued at):是一个时间戳,代表这个 JWT 的签发时间;
jti(JWT ID):是 JWT 的唯一标识。

  1. Public claims,略(不重要)

  2. Private claims,这个指的就是自定义的 claim。比如前面那个结构举例中的 adminname 都属于自定的 claim。这些 claimJWT 标准规定的 claim 区别在于:JWT 规定的 claimJWT 的接收方在拿到 JWT 之后,都知道怎么对这些标准的 claim 进行验证;而 private claims 不会验证,除非明确告诉接收方要对这些 claim 进行验证以及规则才行。

按照 JWT 标准的说明:保留的 claims 都是可选的,在生成 payload 不强制用上面的那些 claim,你可以完全按照自己的想法来定义 payload 的结构,不过这样搞根本没必要:第一是,如果把 JWT 用于认证, 那么 JWT 标准内规定的几个 claim 就足够用了,甚至只需要其中一两个就可以了,假如想往 JWT 里多存一些用户业务信息,比如角色和用户名等,这倒是用自定义的 claim 来添加;第二是,JWT 标准里面针对它自己规定的 claim 都提供了有详细的验证规则描述,每个实现库都会参照这个描述来提供 JWT 的验证实现,所以如果是自定义的 claim 名称,那么你用到的实现库就不会主动去验证这些 claim

最后也是把这个 json 结构做 base64url 编码之后,就能生成 payload 部分的串:

(在线 base64 编码:http://www1.tc711.com/tool/BASE64.htm)

最后看 signature 部分的生成过程。签名是把 headerpayload 对应的 json 结构进行 base64url 编码之后得到的两个串用英文句点号拼接起来,然后根据 header 里面 alg 指定的签名算法生成出来的。算法不同,签名结果不同,但是不同的算法最终要解决的问题是一样的。以 alg: HS256 为例来说明前面的签名如何来得到。按照前面 alg 可用值的说明,HS256 其实包含的是两种算法:HMAC 算法和 SHA256 算法,前者用于生成摘要,后者用于对摘要进行数字签名。这两个算法也可以用 HMACSHA256 来统称。运用 HMACSHA256 实现 signature 的算法是:

正好找到一个在线工具能够测试这个签名算法的结果,比如我们拿前面的 headerpayload 串来测试,并把“secret”这个字符串就当成密钥来测试:

https://1024tools.com/hmac)

最后的结果 B 其实就是 JWT 需要的 signature。不过对比我在介绍 JWT 的开始部分给出的 JWT 的举例:

会发现通过在线工具生成的 headerpayload 都与这个举例中的对应部分相同,但是通过在线工具生成的 signature 与上面图中 的signature 有细微区别,在于最后是否有“=”字符。这个区别产生的原因在于上图中的 JWT 是通过 JWT 的实现库签发的 JWT,这些实现库最后编码的时候都用的是 base64url 编码,而前面那些在线工具都是 bas64 编码,这两种编码方式不完全相同,导致编码结果有区别。

以上就是一个 JWT 包含的全部内容以及它的签发过程。接下来看看该如何去验证一个 JWT 是否为一个有效的 JWT

二.JWT 的验证过程

这个部分介绍 JWT 的验证规则,主要包括签名验证和 payload 里面各个标准 claim 的验证逻辑介绍。只有验证成功的 JWT,才能当做有效的凭证来使用。

先说签名验证。当接收方接收到一个 JWT 的时候,首先要对这个 JWT 的完整性进行验证,这个就是签名认证。它验证的方法其实很简单,只要把 header 做 base64url 解码,就能知道 JWT 用的什么算法做的签名,然后用这个算法,再次用同样的逻辑对 headerpayload 做一次签名,并比较这个签名是否与 JWT 本身包含的第三个部分的串是否完全相同,只要不同,就可以认为这个 JWT 是一个被篡改过的串,自然就属于验证失败了。接收方生成签名的时候必须使用跟 JWT 发送方相同的密钥,意味着要做好密钥的安全传递或共享。

再来看 payloadclaim 验证,拿前面标准的 claim 来一一说明:

iss(Issuser):如果签发的时候这个 claim 的值是“a.com”,验证的时候如果这个 claim 的值不是“a.com”就属于验证失败;
sub(Subject):如果签发的时候这个 claim 的值是“liuyunzhuge”,验证的时候如果这个 claim 的值不是“liuyunzhuge”就属于验证失败;
(Audience):如果签发的时候这个 claim 的值是“[‘b.com’,’c.com’]”,验证的时候这个 claim 的值至少要包含 b.comc.com 的其中一个才能验证通过;
exp(Expiration time):如果验证的时候超过了这个 claim 指定的时间,就属于验证失败;
nbf(Not Before):如果验证的时候小于这个 claim 指定的时间,就属于验证失败;
iat(Issued at):它可以用来做一些 maxAge 之类的验证,假如验证时间与这个 claim 指定的时间相差的时间大于通过 maxAge 指定的一个值,就属于验证失败;
jti(JWT ID):如果签发的时候这个 claim 的值是“1”,验证的时候如果这个 claim 的值不是“1”就属于验证失败;
需要注意的是,在验证一个 JWT 的时候,签名认证是每个实现库都会自动做的,但是 payload 的认证是由使用者来决定的。因为 JWT 里面可能不会包含任何一个标准的 claim,所以它不会自动去验证这些 claim

以登录认证来说,在签发 JWT 的时候,完全可以只用 subexp 两个 claim,用 sub 存储用户的 id,用 exp 存储它本次登录之后的过期时间,然后在验证的时候仅验证 exp 这个 claim,以实现会话的有效期管理。

JWT SSO

场景一:用户发起对业务系统的第一次访问,假设他第一次访问的是系统 A 的 some/page 这个页面,它最终成功访问到这个页面的过程是:

在这个过程里面,我认为理解的关键点在于:

  1. 它用到了两个 cookie(jwtsid)和三次重定向来完成会话的创建和会话的传递;

  2. jwtcookie 是写在 systemA.com 这个域下的,所以每次重定向到 systemA.com 的时候,jwt 这个 cookie 只要有就会带过去;

  3. sidcookie 是写在 cas.com 这个域下的,所以每次重定向到 cas.com 的时候,sid 这个 cookie 只要有就会带过去;

  4. 在验证 jwt 的时候,如何知道当前用户已经创建了 sso 的会话?
    因为 jwtpayload 里面存储了之前创建的 sso 会话的 sessionid,所以当 cas 拿到 jwt,就相当于拿到了 sessionid,然后用这个 sessionid 去判断有没有的对应的 session 对象即可。

还要注意的是:CAS 服务里面的 session 属于服务端创建的对象,所以要考虑 sessionid 唯一性以及 session 共享(假如 CAS 采用集群部署的话)的问题。sessionid 的唯一性可以通过用户名密码加随机数然后用 hash 算法如 md5 简单处理;session 共享,可以用 memcached 或者 redis 这种专门的支持集群部署的缓存服务器管理 session 来处理。

由于服务端 session 具有生命周期的特点,到期需自动销毁,所以不要自己去写 session 的管理,免得引发其它问题,到 github 里找开源的缓存管理中间件来处理即可。存储 session 对象的时候,只要用 sessionid 作为 key,session 对象本身作为 value,存入缓存即可。session 对象里面除了 sessionid,还可以存放登录之后获取的用户信息等业务数据,方便业务系统调用的时候,从 session 里面返回会话数据。

场景二:用户登录之后,继续访问系统 A 的其它页面,如 some/page2,它的处理过程是:

从这一步可以看出,即使登录之后,也要每次跟 CAS 校验 jwt 的有效性以及会话的有效性,其实 jwt 的有效性也可以放在业务系统里面处理的,但是会话的有效性就必须到 CAS 那边才能完成了。当 CAS 拿到 jwt 里面的 sessionid 之后,就能到 session 缓存服务器里面去验证该 sessionid 对应的 session 对象是否存在,不存在,就说明会话已经销毁了(退出)。

场景三:用户登录了系统 A 之后,再去访问其他系统如系统 B 的资源,比如系统 B 的 some/page,它最终能访问到系统 B 的 some/page 的流程是:

这个过程的关键在于第一次重定向的时候,它会把 sid 这个 cookie 带回给 CAS 服务器,所以 CAS 服务器能够判断出会话是否已经建立,如果已经建立就跳过登录页的逻辑。

场景四:用户继续访问系统 B 的其它资源,如系统 B 的 some/page2:

这个场景的逻辑跟场景二完全一致。

场景五:退出登录,假如它从系统 B 发起退出,最终的流程是:

最重要的是要清除 sidcookiejwtcookie 可能业务系统都有创建,所以不可能在退出的时候还挨个去清除那些系统的 cookie,只要 sid 一清除,那么即使那些 jwtcookie 在下次访问的时候还会被传递到业务系统的服务端,由于 jwt 里面的 sid 已经无效,所以最后还是会被重定向到 CAS 登录页进行处理。

方案总结
以上方案两个关键的前提:

  1. 整个会话管理其实还是基于服务端的 session 来做的,只不过这个 session 只存在于 CAS 服务里面;
  2. CAS 之所以信任业务系统的 jwt,是因为这个 jwt 是 CAS 签发的,理论上只要认证通过,就可以认为这个 jwt 是合法的。

jwt 本身是不可伪造,不可篡改的,但是不代表非法用户冒充正常用法发起请求,所以常规的几个安全策略在实际项目中都应该使用:

  1. 使用 https
  2. 使用 http-only 的 cookie,针对 sidjwt
  3. 管理好密钥
  4. 防范 CSRF 攻击。

尤其是 CSRF 攻击形式,很多都是钻代码的漏洞发生的,所以一旦出现 CSRF 漏洞,并且被人利用,那么别人就能用获得的 jwt,冒充正常用户访问所有业务系统,这个安全问题的后果还是很严重的。考虑到这一点,为了在即使有漏洞的情况将损害减至最小,可以在 jwt 里面加入一个系统标识,添加一个验证,只有传过来的 jwt 内的系统标识与发起 jwt 验证请求的服务一致的情况下,才允许验证通过。这样的话,一个非法用户拿到某个系统的 jwt,就不能用来访问其它业务系统了。

在业务系统跟 CAS 发起 attach/validate 请求的时候,也可以在 CAS 端做些处理,因为这个请求,在一次 SSO 过程中,一个系统只应该发一次,所以只要之前已经给这个系统签发过 jwt 了,那么后续 同一系统的 attach/validate 请求都可以忽略掉。

总的来说,这个方案的好处有:

  1. 完全分布式,跨平台,CAS 以及业务系统均可采用不同的语言来开发;
  2. 业务系统如系统 A 和系统 B,可实现服务端无状态
  3. 假如是自己来实现,那么可以轻易的在 CAS 里面集成用户注册服务以及第三方登录服务,如微信登录等。

它的缺陷是:

  1. 第一次登录某个系统,需要三次重定向;
  2. 登录后的后续请求,每次都需要跟 CAS 进行会话验证,所以 CAS 的性能负载会比较大
  3. 登陆后的后续请求,每次都跟 CAS 交互,也会增加请求响应时间,影响用户体验。

转载自:
3 种 web 会话管理的方式
看图理解 JWT 如何用于单点登录

  • JWT

    JWT(JSON Web Token)是一种用于双方之间传递信息的简洁的、安全的表述性声明规范。JWT 作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以 JSON 的形式安全的传递信息。

    20 引用 • 15 回帖 • 2 关注
  • 单点登录

    单点登录(Single Sign On)是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    9 引用 • 25 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...