JWT身份验证技术

概述

在Web系统中,身份验证是十分关键的,它保证访问系统的都是具有合法状态的用户。Web身份验证的方法有很多种,本文将重点介绍一种基于令牌的验证技术JWT,同时与其它的验证技术相对比。
JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,用于创建具有可选签名和加密的数据,于2010年被首次提出。它的出现源于互联网服务中用户认证的需求。
基于会话的认证是用户认证的一个常见方法,它的一般流程是:用户向服务器发送用户名和密码;服务器验证通过后,在当前会话(session)里面保存相关数据,比如用户角色、登录时间等等;服务器向用户返回一个 session_id,写入用户的 cookie;用户随后的每一次请求,都会通过 cookie,将 session_id 传回服务器;服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。
这种模式有一个问题,它的扩展性不好。如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session,而上述认证过程无法做到这一点。一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。
JWT 的原理是,服务器认证以后,生成一个 JSON 对象并发回给用户。以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。返回的JWT主要包括三个部分:Header(头部,用于描述JWT的元数据)、Payload(负载,用来存放实际需要传递的数据)和Signature(签名,防止数据篡改),如下图所示。

JWT的应用场景主要有两种。首先是授权,这是使用 JWT 最常见的场景。通过授权,可以验证发送到服务器的请求是否属于通过身份验证登录的用户,从而可以授予该用户访问系统的权限,继而批准该用户使用获得的 token 访问路由、服务和资源。其次是信息交换。因为 JWT 可以被签名(例如,使用公钥/私钥对),所以能确保发送方是他们所声称的那一方。此外,由于签名是使用 Header 和 Payload 计算的,因此还能验证发送的内容没有被篡改。

特点

JWT主要有这么几个优点。首先,JWT的使用比较灵活。它默认不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。在JWT 不加密的情况下,不能将秘密数据写入 JWT。其次,JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。然而,JWT也存在一些缺陷。它的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。此外,JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

产品介绍与比较

除JWT外,还有一些其它的身份验证技术,比如基于会话的验证、一次性密码等。下面我将逐一介绍它们的特点,同时对比分析JWT。
HTTP协议提供了一些身份验证的机制,其中最基本的一种是把登录凭据随每个请求一起发送到请求标头中。这里的用户名和密码未加密,而是使用一个:符号将用户名和密码串联在一起,形成单个字符串:username:password,再使用 base64 编码这个字符串。它适用于 API 调用以及不需要持久会话的简单身份验证工作流。显然,采用这种技术是很容易被攻击的,因为base64 编码的字符串以纯文本格式发送,可以轻松解码。HTTP还提供了摘要认证技术(HTTP Digest Auth),它的密码以 MD5 哈希形式代替纯文本形式发送的,因此更加安全。验证时,服务器生成一个随机值(称为随机数,nonce),并发回一个 HTTP 401 未验证状态,带有一个WWW-Authenticate标头(其值为Digest)以及随机数。WWW-Authenticate标头使浏览器显示用户名和密码输入框。用户输入凭据后,系统将对密码进行哈希处理,然后与每个请求的随机数一起在标头中发送。最后服务器使用用户名获取密码,将其与随机数一起哈希,然后验证哈希是否相同。上述方法都有一个特点,那就是无状态,服务器不保存用户信息,而是通过随请求发来的凭据来验证用户身份,这一点和JWT十分类似。
会话验证是一种用户状态存储在服务器上的身份验证技术,我在前文的新技术概述部分对它进行过介绍。它不需要用户在每个请求中提供用户名或密码,而是在登录后由服务器验证凭据。如果凭据有效,它将生成一个会话,并将其存储在一个会话存储中,然后将其会话的ID 发送回浏览器。浏览器将这个ID 存储为 cookie,该 cookie 可以在向服务器发出请求时随时发送。与JWT不同,基于会话的身份验证是有状态的。每次客户端请求服务器时,服务器必须将会话放在内存中,以便将ID 绑定到关联的用户。
一次性密码(One Time Password,OTP)通常用作身份验证的确认。OTP 是随机生成的代码,可用于验证用户是否是他们声称的身份。它通常用在启用双因素身份验证的应用中,在用户凭据确认后使用。现代 OTP 是无状态的,可以使用多种方法来验证它们。尽管有几种不同类型的 OTP,但基于时间的 OTP(TOTP)可以说是最常见的类型。它们生成后会在一段时间后过期。TOTP的工作流程是:客户端发送用户名和密码。经过凭据验证后,服务器会使用随机生成的种子生成随机代码,并将种子存储在服务端,然后将代码发送到受信任的系统(经过验证的电子邮件或手机号码等)。用户在受信任的系统上获取代码,然后将其输入回 Web 应用。最后服务器使用存储的种子验证代码,确保其未过期,并相应地授予访问权限。OTP与JWT较为相似,但是它使用受信任的系统,添加了一层额外的保护,从而更加安全。

应用领域解决方案

JWT被广泛应用于各个厂商的软件产品中。比如,Google Cloud的API Gateway就使用了JWT技术。API Gateway是一个分布式API管理系统,可以通过在所有服务之间保持一致且定义明确的 REST API 来安全地访问各种服务,而不考虑服务实现。API Gateway支持使用 JWT 对用户进行身份验证。具体做法是将身份验证代码添加到客户端应用,客户端将HTTP 请求的授权标头中的JWT发送给后端,API Gateway 使用 JWT 颁发者的JSON Web密钥集(JWKS)高效地验证 JWT。此外,阿里云的网盘与相册服务(PDS)也支持JWT应用。用户可以在PDS 控制台创建自定义域和JWT应用,利用RSA算法创建一对公私钥,将公钥保存到PDS服务端,私钥保存到JWT应用服务端。JWT应用服务端将数据进行编码并用私钥进行签名生成JWT字符串,然后发送给PDS服务端。PDS服务端使用公钥验证JWT字符串合法后,返回access_token给JWT应用服务端,JWT应用服务端可以通过access_token来调用PDS服务端提供的API。
可以看到,JWT主要用于访问远程服务时的身份验证,但不同应用验证JWT的方法可能会有一些不同,就像上面的例子中,API Gateway使用JWKS验证JWT,而PDS使用RSA算法和公私钥进行验证。