咨询
交流群
电话

授权及认证机制:Session、JWT与OAuth 2

2020-11-11 13:17 阅读

Session模式

浏览器主流授权模式采用Session模式。

participant 浏览器 participant 服务器 浏览器->服务器: 用户登录 Note right of 浏览器: 提供用户名、密码 服务器->服务器: 产生并保存 Session ID Note right of 服务器: Session ID 与用户关联 服务器-->浏览器: 返回 Session ID 浏览器->浏览器: 自动将 Session ID 保存到 Cookie 浏览器->服务器: 再次访问 Note right of 浏览器: 自动携带 Cookie (包含 Session ID) 服务器->服务器: 检查 Session ID 是否存在 Note right of 服务器: 存在则自动匹配相应用户并刷新 Session 过期时间 服务器-->浏览器: 返回响应结果

特点

  • 服务器端产生Session ID,浏览器每次访问时携带Session ID作为身份标识。
  • Session过期时间一般为30分钟,每次访问会自动刷新过期时间。

优势

  • 30分钟无操作Session自动过期,增强安全性。
  • 服务器可将Session ID的Cookie设置成HttpOnly,可以防止js从Cookie读取Session ID。避免XSS漏洞获取Session ID。
  • 符合大部分人的使用习惯。

缺点

  • Session信息需要在服务器端保存一份副本。在多服务器(如集群)的情况下,需要进行Session同步。
  • Session只要保持刷新,则永久有效(除非用户主动退出)。如果攻击者从浏览器或者从数据传输中窃取到Session ID,则可以保持该Session ID永久有效。

JWT模式

JWT全称为:JSON Web Token。适合分布式的授权模式。用户登录成功后,认证服务器会返回一个带有签名认证的JWT。JWT保存在客户端(如浏览器或APP),每次访问应用服务器时,将JWT放到http的header里面。不同于Session的集中管理,JWT自身就包含了用户信息,每台应用服务器可以自己独自判断JWT是哪个用户、是否合法、是否过期,无需再向认证服务器确认,也没有Session同步的问题。JWT至少包含以下信息:

  • sub: Subject,一般为用户ID。
  • exp: Expiration Time。过期时间。
  • signature: 签名。用于验证JWT是否合法。
participant 用户浏览器 participant 应用服务器 participant 认证服务器 用户浏览器->应用服务器: 访问登录页面 应用服务器-->用户浏览器: 返回登录页面 用户浏览器->认证服务器: 登录请求 Note right of 用户浏览器: 提供用户名、密码 认证服务器-->用户浏览器: 提示用户登录成功 Note left of 认证服务器: 返回JWT,包含用户ID、过期时间和签名 用户浏览器->应用服务器: 获取数据 Note right of 用户浏览器: header中携带JWT 应用服务器->应用服务器: 解析JWT Note right of 应用服务器: 通过签名验证JWT是否合法,判断是否过期,并获取用户ID 应用服务器-->用户浏览器: 返回数据

Access Token 和 Refresh Token

Access Token需要放到http的header里面,用于访问各种资源。由于使用频繁且使用场景复杂,泄露风险较大,因此过期时间会比较短,比如30分钟。如果每30分钟让用户重新登录一次,显然用户体验极差。所以认证服务器在返回Access Token时,还会同时返回一个Refresh Token。Refresh Token平时不使用,只用于从认证服务器获取新的Access Token,会安全很多。

Refresh Token 有效期

Refresh Token的有效期通常为30天。但在很多手机APP应用,并不想让用户每30天就登录一次。而将有效期设置为1年甚至2年,又觉风险稍大。

给Refresh Token增加一个认证有效期(如30天),有效期则可以较长(如1年)。只要在认证有效期内刷新,则可以获取新的Refresh Token,认证有效期重新计算,有效期按前一个Refresh Token计算;如果过了认证有效期再刷新,则Refresh Token不刷新,且只能获取到Remember Me权限的Access Token。

只要用户在30天内使用了APP,则自动刷新Refresh Token,无需再次登录。直到1年后刷新有效期到期,才需要再次登录。

刷新Access Token时,自动返回新的Refresh Token。无需专门刷新Refresh Token。

Remember Me(记住我)

并且在浏览网站时,通常会有两个级别的权限,一种是记住我的权限,一种是登录的权限。

给Access Token增加Remember Me标识。如果Refresh Token过了认证有效期再刷新,则只能获取到Remember Me权限的Access Token。

30分钟自动过期

使用浏览器访问时,不能长期保存Refresh Token(安全考虑)。必须在浏览器关闭或用户退出时删除Refresh Token。

要模拟Session 30分钟过期的模式,可以规定在浏览器访问时,返回的Refresh Token的认证有效期为30分钟,用户操作后再刷新Refresh Token,以维持Refresh Token的有效性。

Refresh Token可以保存到Cookie或者sessionStorage,确保关闭页面时Refresh Token会被删除。

刷新Token时机

可以每5分钟自动刷新Token(不需要自动过期时)。如果需要自动过期,则要根据用户的操作刷新。用户每次操作时,如距离上次刷新Token时间超过5分钟,则自动刷新Token。

每5分钟刷新一次Token,可以更准确的监测在线人数,也更满足模拟自动过期的要求。如果觉得5分钟太短,可以尝试10分钟。

重新登录时机

为了避免用户正在操作的时候,突然Refresh Token到期重新登录,应该在Refresh Token到期的最后30天中,用户重新打开APP时,要求用户重新登录。

在线人数

如果需要监控在线人数,则认证服务器应该将登录的数据写入数据库。由于Access Token的有效期很短,通常需要5分钟到应用服务器刷新一次。因此可以很容易的监控到当前的在线人员。

如何将众多的Access Token和Refresh Token与某个用户的登录关联起来,关键在于用户登录时需要产生一个登录ID,将这个ID写入所有后续的Refresh Token和Access Token中,则可以知道任意一个Token是属于哪个用户的哪次登录。

Refresh Token泄露监控

由于同一系列的Refresh Token都拥有同一个登录ID,而每个Refresh Token都会在5分钟或者更长的时间内刷新Access Token,因此只要有相同登录ID的Refresh Token在5分钟内刷新了两次Access Token,就可判顶Refresh Token已经泄露。

强制下线

Refresh Token强制下线:需要在认证服务器记录所有已发布并在刷新有效期内的Refresh Token(可以只记录登录ID),每次刷新token时,检查登录ID是否被强制下线。由于Refresh Token每5分钟刷新一次,且Access Token有效期为30分钟,这种下线不是即时生效的。但代价较低,只要在认证服务器处理即可。

Access Token强制下线:时效性高,立即下线,但代价也较高。需要每个应用服务器在验证JWT时,向认证服务器进行校验,以检查Access Token对应的登录ID是否被强制下线。这已经违反了JWT自描述的设计初衷,且性能损耗有可能非常大。不过在认证服务器和应用服务器为同一服务器的情况下,还是可用的。

OAuth 2

主要用于第三方登录,如微信登录、QQ登录、微博登录等。所有的应用都必须先到认证服务器备案(如微信、QQ、微博),获取客户端ID(Client ID)和客户端密钥(Client Secret)。

participant 用户浏览器 participant 应用服务器 participant 认证服务器 用户浏览器->应用服务器: 登录请求 应用服务器-->用户浏览器: 重定向至认证服务器 Note left of 应用服务器: 提供Client ID 用户浏览器->认证服务器: 允许授权 Note right of 用户浏览器: 提供用户名、密码和Client ID 认证服务器-->用户浏览器: 返回授权码并重定向至应用服务器 Note left of 认证服务器: 授权码授权码在浏览器中传递,可能被泄漏,所以只能用于获取Token 用户浏览器->应用服务器: 提供授权码 应用服务器->认证服务器: 获取Token Note right of 应用服务器: 提供授权码、Client ID、Client Secret 认证服务器-->应用服务器: 返回Token 应用服务器->认证服务器: 获取用户信息 Note right of 应用服务器: 提供Token 认证服务器-->应用服务器: 返回Token对应用户的信息 应用服务器-->用户浏览器: 提示用户登录成功