- A+
1.定义
JSON Web Tokens (JWTs) 是一种开放标准(RFC 7519),定义了一种紧凑、自包含的方式来安全地在各方之间传输信息。JWT主要由三个部分组成,即Header(头部)、Payload(载荷)和Signature(签名),这三部分之间通过.分隔。
JWT的内容采用Base64编码,可以直接嵌入到HTTP请求头或者URL查询参数中,因其具有可读性、自包含和防篡改的特点而广泛应用在身份验证和授权场景中。
1.1 Header:
描述了所使用的JWT类型(通常为JWT)以及签名算法(如HS256、RS256等)。
-
typ: 表明这是一个JWT(固定为 “JWT”)。
-
alg: 指定用于签署JWT的算法,如 “HS256”(HMAC SHA-256)或 “RS256”(RSA SHA-256)等。
{ "alg": "HS256", "typ": "JWT" }
1.2 Payload:
包含了实际要传递的数据,可以是任意的JSON对象,包含一组称为声明(claims)的数据。一般为用户身份信息(如用户ID、角色、权限等)和其他自定义声明,还包含一个exp(Expiration Time)字段来设置JWT的有效期以及其他元数据。声明分为三种类型:
1.2.1 Registered Claims(注册声明)
这些是预先定义好的标准声明,虽然不是必须的,但在JWT规范中建议使用。它们提供了一套通用的信息,有助于JWT的标准化处理。常见的注册声明包括:
- iss(issuer):签发JWT的实体。
- sub(subject):JWT所面向的用户或主题。
- aud(audience):预期接收JWT的受众。
- exp(expiration time):JWT过期时间,在此时间之后JWT应被视为无效。
- nbf(not before):JWT生效时间之前,不应被接受处理的时间点。
- iat(issued at):JWT的创建时间。
- jti(JWT ID):JWT的唯一标识符,可用于防止重放攻击。
1.2.2 Public Claims(公共声明)
公共声明是预留用于行业共识的标准声明,虽然目前并未正式注册到IANA JSON Web Token Registry,但可以在IANA JSON Web Token Registry 查看已注册的声明集。如果没有官方注册,但多个项目间需要共享相同的声明,可以选择将声明名称以特定前缀
进行命名,避免冲突。
1.2.3 Private Claims(私有声明)
私有声明是用于应用内部约定的自定义声明,开发者可以根据自己业务需求自由定义。例如,可以包含用户ID (userId)、用户角色 (role)、邮箱地址 (email) 等任何想要在JWT中传递的信息。
{ "sub": "1234567890", "name": "John Doe", "admin": true, "iat": 1516239022, "jti": "1pmysetgujcoden", "exp": 1516242622, "scope": [ "read", "write" ] }
来源: 码农Academy的博客
作者: 码农Academy
链接: https://www.coderacademy.online/article/jwtuse.html
本文章著作权归作者所有,任何形式的转载都请注明出处。
Payload部分默认是不加密的,一定不要将隐私信息存放在 Payload 当中!!!
1.3 Signature:
Signature部分用于确保JWT在传输过程中没有被篡改,它是通过对前两部分(Header和Payload编码后的字符串)使用Header中指定的加密算法以及一个共享或私有的密钥进行签名计算得到的。签名确保了只有知道该密钥的实体才能创建有效的JWT,并且任何人都可以验证JWT的完整性和来源的真实性。
生成签名的计算公式如下:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
算出签名以后,把Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用”点”(.)分隔,这个字符串就是JWT 。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMiwianRpIjoiMXBteXNldGd1amNvZGVuIiwiZXhwIjoxNTE2MjQyNjIyLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXX0.eW91cmUtYXV0aG9yaXphdGlvbi1zaWduYXR1cmU=
对于这段字符串,我们在JWT官网中,使用它的解码器进行解码,就可以得到Header、Payload、Signature这三部分。
2.如何基于JWT进行身份验证?
2.1 JWT生成
服务器首先构建JWT的Header和Payload,并分别进行Base64编码。使用Header中指定的签名算法(比如HMAC SHA-256或RSA)对编码后的Header和Payload进行签名,生成Signature。将Header、Payload和Signature连接在一起,形成完整的JWT字符串。
2.2 JWT发放
用户通过用户名/密码登录后,服务器验证用户身份无误,便生成JWT并将其返回给客户端。客户端可以将JWT存储在浏览器的LocalStorage、SessionStorage中,或者作为Bearer Token放在Authorization请求头中。
这里需要注意,将JWT存储在浏览器的localStorage中,相较于存储在Cookie中,可以降低CSRF(跨站请求伪造)的风险。因为CSRF攻击依赖于浏览器自动附带在请求中的Cookie,而localStorage中的数据不会自动包含在普通的HTTP请求头部。
至于携带JWT的方式,建议将JWT放在HTTP Header的Authorization字段中,采用Bearer Token的形式:
Authorization: Bearer
这种做法允许在无状态的RESTful API中方便地进行身份验证,服务器可以根据请求头中的JWT来验证请求发起者的身份和权限。同时,这种方式也便于实现JWT的刷新与撤销。
2.3 JWT使用
在后续的请求中,客户端将JWT随HTTP请求一起发送给服务器。服务器接收到JWT后,对其进行解码和验证Signature,如果Signature正确,则说明JWT未被篡改,并且仍处于有效期内。根据Payload中的信息,服务器可以确定用户身份以及相关权限,从而做出相应处理。
2.4 JWT过期与刷新
JWT有一定的生命周期,过了指定的exp字段时间后,服务器不再接受该JWT。对于长时间交互的场景,可以设计Refresh Token机制,当JWT即将过期时,客户端使用Refresh Token去服务器申请新的JWT,以延长用户会话的有效期。
3 JWT安全性
3.1 JWT如何防止被篡改?
JWT通过签名(Signature)来防止被篡改。数字签名的机制确保了JWT的内容一旦被篡改,服务端就能检测出来,从而拒绝非法的请求。
当客户端带着JWT向服务器发送请求时,服务器会先将接收到的JWT按.分割成Header、Payload和Signature三部分。服务器使用与生成签名时相同的密钥和算法,根据接收到的Header和Payload重新计算签名。如果重新计算出的签名与接收到的Signature一致,那么可以认为JWT在传输过程中未被篡改;如果不一致,则说明JWT已被篡改。
当然,JWT通过这种方式防止被篡改的一个大前提就是,秘钥(Secret Key)是安全的,如果秘钥泄漏,那就可以更改Header、Payload,然后利用这个秘钥生成一个Signature就可以了,那么就不安全了。所以,我们一定要确保服务端使用的密钥是安全的,并且严格保密,不对外泄露。
3.2 如何加强JWT的安全性?
作为使用者,防止JWT被篡改的主要措施并不直接体现在客户端的操作上,而是依赖于服务端的安全设计和实施。我们可以通过以下方面考虑,加强JWT的安全性:
3.2.1 服务端安全策略:
- 使用安全密钥(Secret Key):确保服务端使用的密钥是安全的,并且严格保密,不对外泄露。密钥用于签署和验证JWT,一旦密钥泄露,可能导致JWT被伪造或篡改。
- 选择强签名算法:采用安全强度足够高的签名算法,例如HS256、RS256等,这些算法能有效地确保JWT的完整性。
3.2.2 正确的签名流程:
确保所有签发的JWT都经过服务器端签名,并且每次验证JWT时都要使用相同的密钥和算法进行验证。
3.2.3 设置合理的Token有效期:
设置JWT的有效期(exp claim)限制,避免JWT长时间有效,减少恶意攻击者篡改JWT后继续使用的机会。
3.2.4 设计严谨的身份验证逻辑:
在服务端验证JWT时,除了验证签名之外,还应对Payload中的其他重要声明(如用户ID、权限、过期时间等)进行校验。
3.2.5 安全传输:
即使JWT无法直接阻止在网络中被截获,但可以通过HTTPS等安全传输层协议来保证JWT在传输过程中的安全。
3.2.6 客户端存储方式:
尽管JWT被篡改的风险主要集中在网络传输阶段,但在客户端存储JWT时,推荐使用localStorage而非Cookie(尤其是考虑到防止CSRF攻击),同时也可以考虑对敏感信息加密存储。
4 JWT相较于Cookie和Session的优缺点
4.1 JWT的优点:
4.1.1 无状态性:
JWT是无状态的,这意味着服务器不需要存储会话状态信息,减轻了服务器端的存储负担,特别适合分布式环境下的部署,因为JWT可以在集群内的任何节点验证,无需保持会话状态同步。
4.1.2 易于扩展:
JWT可以轻易地在不同域名的服务之间传递,适用于微服务架构和跨域应用。因为它包含了所有必要的用户信息,所以在服务间跳转时无需再次验证用户身份。
4.1.3 减少网络延迟:
每个请求都包含了认证所需要的所有信息,减少了服务器查询数据库或存储服务以验证用户状态的次数,从而提高了效率。
4.1.4 离线验证:
在某些条件下,JWT的payload允许客户端在一段时间内离线状态下使用已验证过的令牌进行操作。
4.2 JWT的缺点:
4.2.1 安全性考量:
- JWT中的数据默认情况下是Base64编码的,虽然可以加密但并非强制要求,这使得在不加密的情况下,JWT不适合存储敏感信息。相比而言,Session存储在服务器端,安全性更高。
- 当JWT被盗取时,攻击者可以在有效期内持续使用,直到token过期或被撤销。撤销JWT通常需要额外的机制,例如黑名单。
4.2.2 大小与性能:
- JWT的体积相对较大,特别是当包含更多信息时。每次HTTP请求都需要携带JWT,可能会增加请求头的大小,尤其是在移动网络环境下,可能会对带宽和流量有较大影响。
4.2.3 有限的撤销控制:
- 一旦JWT发放出去,除非在服务器端维护黑名单或使用可撤销的Token机制(如JWT ID Token配合OpenID Connect协议),否则无法立即废弃或更改Token中的权限信息。
4.2.4 生命周期管理:
- 因为JWT自带有效期,当用户登出时,由于JWT无法立即失效,可能需要额外的方法来确保用户退出后不能再使用之前的Token进行操作。
5 与Cookie和Session对比为什么要使用JWT?
-
Cookie和Session存储在服务器端,安全性较高,尤其对于敏感数据的存储更为合适。服务器可以根据需求随时修改或销毁Session,便于管理用户会话状态。
-
Session会导致服务器负载增加,因为每个活跃用户都会占用一定的服务器内存资源。而JWT则将用户状态分散到客户端,降低了服务器端的压力。
-
Cookie依赖于客户端浏览器支持,并且可能面临跨站请求伪造(CSRF)的风险,需要采取额外措施防范。JWT通过在HTTP Header中以Bearer Token形式发送,可以规避这些风险。
-
Session的生命周期受服务器控制,可以灵活调整会话长度或强制登出用户。而JWT的生命周期由Token自身携带的exp属性决定,需要提前预设好有效期限。
6 JWT应用场景
-
身份验证:JWT通常用于用户登录认证,服务器在验证用户凭据成功后,生成一个带有用户特定信息和过期时间的JWT,客户端收到后,在后续请求中将其作为凭证发送给服务器,服务器仅需验证JWT的签名即可确认用户身份,无需持久化存储会话信息。
-
授权:JWT可以携带权限声明,服务器通过解析JWT中的声明,决定用户是否有权访问特定资源或执行某些操作。
-
单点登录(Single Sign-On, SSO):由于JWT可以跨域使用,因此在多个子系统间轻松实现SSO功能,用户在一个系统登录后,其JWT可在多个系统间传递,实现无缝登录体验。
在高并发、分布式、跨域及移动端场景下,JWT常因其实现的简洁性和高效性而得到青睐,而在注重数据安全性、复杂会话管理和易控制的场景下,Session可能会是更好的选择。同时,很多现代应用采用了混合策略,结合两者的优势来优化用户体验和系统安全性。
7 结论
JWT(JSON Web Tokens)作为一种轻量级且灵活的身份验证和授权机制,在现代web服务及移动应用中得到了广泛应用。它允许服务器端通过加密签名的方式向客户端发放安全的、自包含的令牌,这些令牌可以携带必要的用户身份信息和权限声明,而且由于其无需持久化存储的特性,非常适合于微服务架构下的无状态通信场景。
总结起来,采用JWT进行身份验证具有以下优点:
- 安全性: 通过密钥签名保证令牌的安全性,防止篡改。
- 高效性: 状态less设计减少了服务器端存储负担,提升了系统的可扩展性和响应速度。
- 跨域友好: JWT可轻松应用于多个域名或子系统间的认证需求。
- 自包含性: 令牌自身携带了足够的用户信息,减轻了服务器端查询数据库的频率。
- 有效期可控: 可以在JWT中设置过期时间,从而控制用户的登录会话持续时间。
同时需要注意的是,JWT并非适用于所有场景,尤其是在敏感数据的处理上,因为一旦令牌被截获,攻击者在有效期内可以持续使用。因此,在实施JWT方案时,应充分考虑其适用范围,并配合合适的刷新策略、黑名单机制以及其他安全措施,确保系统的整体安全性和用户体验。总体来说,正确地理解和运用JWT对于构建高效、安全的应用程序至关重要。