一、前言
在当今的网络应用开发中,用户认证是一个至关重要的环节。随着技术的发展,传统的基于Session的认证方式逐渐暴露出一些问题,如跨域限制、服务器性能瓶颈等。而JSON Web Token(JWT)作为一种新兴的认证解决方案,以其无状态、可扩展性强、易于跨域等优势,受到了广泛的关注和应用。
二、JWT基本介绍
JWT(JSON Web Token)是一种开放标准(RFC 7519),它允许在各方之间安全地将信息作为JSON对象传输。JWT本质上是一个字符串,由三部分组成:头部(Header)、负载(Payload)和签名(Signature),各部分之间用英文的“.”分隔。其具有如下特点:
- 无状态:服务器无需存储客户端的会话信息,所有信息都包含在Token中,减轻了服务器的存储压力。
- 自包含:Token中包含了用户的相关信息,服务器可以通过解析Token获取用户信息,无需查询数据库。
- 跨域支持:与基于Cookie的认证方式不同,JWT不依赖Cookie,因此在跨域场景下不会受到浏览器同源策略的限制。
- 安全性:通过加密算法对Token进行签名,确保了Token在传输过程中的完整性,防止被篡改。
三、JWT的原理机制
(一)组成
- 头部(Header):通常由两部分组成,
alg
表示签名算法,如“HMAC SHA256”或“RSA”;typ
表示令牌的类型,即JWT。例如:
{
"alg": "HS256",
"typ": "JWT"
}
将上述JSON对象使用Base64 URL算法转换为字符串,得到头部编码。
- 负载(Payload):包含声明,声明是关于实体(通常是用户)和其他数据的声明。有三种类型的声明:注册声明、公共声明和私有声明。例如:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
● Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐。比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。
● Public claims : 可以随意定义。
● Private claims : 用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明。
这段文本是对JWT(JSON Web Token)中不同类型声明的解释说明。主要内容包括:
Registered claims(注册声明)
这是一组预定义的声明。它们不是强制要求的,但被推荐使用。
一些常见的例子包括:
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 的唯一标识。
Public claims(公共声明)
可以随意定义。这意味着开发者可以根据自己的需求来创建和使用这些声明,只要确保这些声明的名称不会与注册声明冲突即可。它们是公开的,用于在各方之间传递自定义信息。
Private claims(私有声明)
用于在同意使用它们的各方之间共享信息。这些声明不是注册的或公开的,具有一定的私密性,只在参与的各方之间使用,以满足特定的业务或应用需求。
对负载部分也进行Base64 URL编码。
- 签名(Signature):为了创建签名部分,需要采用编码后的头部、编码后的负载和一个密钥。密钥通常是通信双方提前协定的,对于HASH签名方式,使用一个字符串即可。例如:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
如采用非对称加密算法RSA,采用公钥与私钥进行加密和解密,从而不仅可以防止篡改,还是确定发生方的身份。
实际应用中,大多采用的是Hash方式,简单易用。
(二)工作流程
- 生成令牌:
- 用户通过用户名和密码向服务器发送登录请求。
- 服务器验证用户身份信息,验证成功后,使用JWT算法,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进行Base64编码拼接后签名,生成一个JWT Token。
- 传输令牌:
- 服务器将生成的JWT Token返回给客户端,客户端通常会将Token存储在localStorage或cookie中。
- 当客户端之后向服务器发送请求时,需要在HTTP请求头中的Authorization字段中携带JWT Token,格式为“Bearer ”。
- 验证令牌:
- 服务器接收到带有JWT Token的请求后,提取Token中的头部、负载和签名信息。
- 使用相同的密钥和算法对头部和负载进行签名,并比较生成的签名与Token中的签名是否匹配。如果匹配,则Token有效;否则,拒绝请求。
- 服务器还会验证负载中的声明,例如检查Token是否过期、发行者是否可信等。
四、使用JWT的注意事项
- 安全性:
- 使用强密钥和算法:选择高强度的加密算法和密钥,防止Token被破解。
- 防止信息泄露:避免在JWT中存储敏感信息,如用户的密码等。因为JWT默认情况下是未加密的,只是Base64编码,所以任何人都可以解读其内容。
- 采用HTTPS协议:在传输过程中使用HTTPS协议,确保数据在传输过程中的安全性,防止中间人攻击。
- 防范XSS攻击:由于JWT通常存储在客户端,如localStorage或cookie中,容易受到XSS攻击的威胁。因此,需要采取措施防范XSS攻击,如对用户输入进行严格过滤和转义等。
- 防御重放攻击:可以通过在Token中添加时间戳、随机数等信息,以及在服务器端维护一个已使用的Token列表等方式来防御重放攻击。
- 令牌过期与刷新机制:
- 在负载(Payload)中加入
exp
(过期时间)字段,定义令牌的生命周期。当令牌过期后,接收方应拒绝处理该令牌。 - 实现刷新机制,允许客户端在访问令牌过期后,使用刷新令牌获取新的访问令牌,而无需用户重新输入用户名和密码。但要注意刷新令牌的安全性、限制刷新次数以及设置合理的过期时间。
- 在负载(Payload)中加入
- 单点登录(SSO)的风险:
- 在单点登录场景下,如果一个用户的JWT被盗用,可能会导致多个系统受到影响。因此,需要采取额外的安全措施来保护单点登录系统。
- 令牌撤销机制:
- 由于JWT是无状态的,服务器无法直接撤销一个有效的JWT。可以采用黑名单机制,将已撤销的Token记录在黑名单中,当客户端请求时,服务器检查Token是否在黑名单中。
- 实际应用中有一种相对便捷的处理方式,即不采用统一的密钥,而是将用户的密码作为密钥,当发现令牌被非法获取和使用时,可以通过修改用户密码这一常规操作,实现令牌的撤销。
- 存储和传输的安全性:
- 如果将JWT存储在localStorage中,需要注意localStorage的安全性较低,容易受到XSS攻击。可以考虑使用HttpOnly和Secure标志设置的cookie来存储JWT,以提高安全性。
- 及时更新库和依赖:
- 关注JWT相关库和依赖的更新,及时修复已知的安全漏洞。
五、常见问题
- 如何处理JWT过期问题?
- 客户端在每次发送请求时,可以先检查JWT是否过期。如果过期,则尝试使用刷新令牌获取新的访问令牌。
- 服务端在接收到请求时,也会验证JWT的过期时间。如果过期,返回相应的错误信息,客户端根据错误信息提示用户重新登录或自动刷新令牌。
- JWT是否适合存储大量数据?
- JWT不适合存储大量数据,因为Token的大小会增加,导致每次请求的HTTP头信息变大,影响性能。JWT主要用于存储用户身份信息和一些必要的声明,而不是大量的业务数据。
- 如何防止JWT被篡改?
- JWT通过签名来确保数据的完整性。只有使用正确的密钥和算法对JWT进行签名验证,才能确保JWT未被篡改。因此,必须确保密钥的安全性,防止密钥泄露。
- JWT在分布式系统中的优势?
- JWT在分布式系统中具有明显的优势。由于其无状态的特点,各个服务节点无需共享会话信息,减少了服务之间的耦合性,提高了系统的可扩展性。
如果您在阅读本文时获得了帮助或受到了启发,希望您能够喜欢并收藏这篇文章,为它点赞~
请在评论区与我分享您的想法和心得,一起交流学习,不断进步,遇见更加优秀的自己!