JWT(JSON Web Token)是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON对象的形式安全地传输信息。JWT通常用于身份验证和授权目的,因为它可以使用JSON对象在各方之间安全地传输信息
官网地址:https://jwt.io/
0.介绍
通俗地说,JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串中,然后进行编码后得到一个JWT token
,并且这个JWT token
带有签名信息,接收后可以校验是否被篡改,所以可以用于在各方之间安全地将信息作为Json对象传输。
0.1.JWT的应用场景
身份验证 : 当用户成功登录时,服务器会生成一个JWT并将其发送给客户端。客户端在后续请求中将JWT附加到HTTP请求头中,以此来证明用户的身份。
授权 : JWT中可以包含用户的权限信息,这样服务器可以根据这些信息决定用户是否被允许访问某些资源。
信息传递 : 除了用户的身份和权限外,JWT还可以用来携带其他有用的信息,如用户的偏好设置等。
在 java 中 常与 Spring Security
框架配合使用
JWT的认证流程如下:
- 首先,前端通过Web表单将自己的用户名和密码发送到后端的接口,这个过程一般是一个POST请求。建议的方式是通过SSL加密的传输(HTTPS),从而避免敏感信息被嗅探
- 后端核对用户名和密码成功后,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进行Base64编码拼接后签名,形成一个JWT Token,形成的JWT Token就是一个如同lll.zzz.xxx的字符串
- 后端将JWT Token字符串作为登录成功的结果返回给前端。前端可以将返回的结果保存在浏览器中,退出登录时删除保存的JWT Token即可
- 前端在每次请求时将JWT Token放入HTTP请求头中的Authorization属性中(解决XSS和XSRF问题)
- 后端检查前端传过来的JWT Token,验证其有效性,比如检查签名是否正确、是否过期、token的接收方是否是自己等等
- 验证通过后,后端解析出JWT Token中包含的用户信息,进行其他逻辑操作(一般是根据用户信息得到权限等),返回结果
0.2.优点
这种基于token的认证方式相比传统的session认证方式更节约服务器资源,并且对移动端和分布式更加友好。其优点如下:
-
支持跨域访问:cookie是无法跨域的,而token由于没有用到cookie(前提是将token放到请求头中),所以跨域后不会存在信息丢失问题
-
无状态:token机制在服务端不需要存储session信息,因为token自身包含了所有登录用户的信息,所以可以减轻服务端压力
-
更适用CDN:可以通过内容分发网络请求服务端的所有资料
-
更适用于移动端:当客户端是非浏览器平台时,cookie是不被支持的,此时采用token认证方式会简单很多
-
无需考虑CSRF:由于不再依赖cookie,所以采用token认证方式不会发生CSRF,所以也就无需考虑CSRF的防御
1.JWT的结构
JWT是一种自包含的令牌格式。JWT由三个部分组成:头部、载荷和签名。
1.1.头部 (Header)
头部通常包含两个部分:
typ
: 表示该令牌的类型,通常是“JWT”。alg
: 指定签名算法,例如 HMAC SHA-256 或 RSA。
头部通常以JSON格式书写,并经过Base64Url编码。
1.2.载荷 (Payload)
载荷包含了需要作为声明传输的信息。这些声明可以分为三类:
- 标准声明:由JWT规范定义的声明。
iss
(issuer): 发行者。sub
(subject): 主题,通常是指用户ID。aud
(audience): 接收者,即令牌的预期受众。exp
(expiration time): 过期时间。nbf
(not before): 该时间之前不可使用。iat
(issued at): 发行时间。jti
(JWT ID): 一个唯一的标识符,用于防止重放攻击。
- 私有声明:由发行者和接收者约定的声明,例如用户的角色或权限等。
- 公共声明:虽然不是JWT规范的一部分,但可以在任何JWT中使用。
载荷也是经过Base64Url编码的。
1.3.签名 (Signature)
签名部分保证了JWT的完整性和安全性。签名通过将头部和载荷进行编码并使用指定的算法(如HMAC SHA-256 RSA或ECDSA)进行计算得到。签名确保了:
- 令牌没有被篡改。
- 令牌是由可信的一方发行的。
签名部分同样经过Base64Url编码。
1.4.JWS, JWK
JWS
,也就是JWT Signature
,其结构就是在之前nonsecure JWT的基础上,在头部声明签名算法,并在最后添加上签名。创建签名,是保证jwt不能被他人随意篡改。我们通常使用的JWT一般都是JWS
为了完成签名,除了用到header
信息和payload
信息外,还需要算法的密钥,也就是secretKey。
加密的算法一般有2类:
- 对称加密:secretKey指加密密钥,可以生成签名与验签
- 非对称加密:secretKey指私钥,只用来生成签名,不能用来验签(验签用的是公钥)
JWT的密钥或者密钥对,一般统一称为JSON Web Key
,也就是JWK
到目前为止,jwt的签名算法有三种:
- HMAC【哈希消息验证码(对称)】:HS256/HS384/HS512
- RSASSA【RSA签名算法(非对称)】 :(RS256/RS384/RS512)
- ECDSA【椭圆曲线数据签名算法(非对称)】 :(ES256/ES384/ES512)
2.实现与工具
2.1.jjwt (Java JWT)
GitHub 仓库地址:https://github.com/jwtk/jjwt
这是一个基于Java的库,用于在JVM和Android平台上创建和验证JSON Web Tokens (JWTs)和JSON Web Keys (JWKs)。
它支持多种JOSE工作组的RFC规范:
- RFC 7519: JSON Web Token (JWT)
- RFC 7515: JSON Web Signature (JWS)
- RFC 7516: JSON Web Encryption (JWE)
- 等等
2.1.1.导入Maven依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
2.1.2.测试代码
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bi