1. 什么是JWT令牌
JSON Web Token(JWT)是一种使用JSON格式传递数据的网络令牌技术,它是一个开放的行业标准(RFC 7519),它定义了一种简洁的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任,它可以使用HMAC算法或使用RSA的公钥/私钥对来签名,防止内容篡改。官网:JWT
使用JWT可以实现无状态认证:
传统的基于session的方式是有状态认证,用户登录成功将用户的身份信息存储在服务端,这样加大了服务端的存储压力,并且这种方式不适合在分布式系统中应用。
如果是基于令牌技术在分布式系统中实现认证则服务端不用存储session,可以将用户身份信息存储在令牌中,用户认证通过后认证服务颁发令牌给用户,用户将令牌存储在客户端,去访问应用服务时携带令牌去访问,服务端从jwt解析出用户信息。这个过程就是无状态认证。
2. JWT令牌的优缺点
2.1 JWT令牌的优点
- 紧凑性:JWT 是 URL 安全的,可以在 URL 中传输。
- 自包含:JWT 包含了验证用户身份所需的信息,因此可以减少服务器的数据库查询次数。
- 跨域支持:由于 JWT 是无状态的,适合在跨域场景中使用。
- 无状态:JWT 是无状态的,不需要在服务器端保存会话信息,这使得它非常适合微服务架构。
2.2 JWT令牌的缺点
- 无状态导致的难以撤销:一旦 JWT 发放出去,除非它过期或在服务器上设置一个黑名单,否则无法撤销它。
- 较大的 Payload 可能导致开销:如果 JWT 包含太多信息,可能会影响传输效率。
3. JWT令牌的组成
JWT令牌由三部分组成,Header(头部)、Payload(负载)、Signature(签名);
它们由点 (.
) 分隔,结构如下:
Header.Payload.Signature
3.1 Header(头部)
Header 通常包含两部分信息:
- 类型:表示这是一个 JWT(通常写为 "JWT")。
- 签名算法:例如 HMAC SHA256 或 RSA。
一个典型的 Header 如下:
{
"alg": "HS256",
"typ": "JWT"
}
这个 JSON 被 Base64 编码后形成 JWT 的第一部分。
3.2 Payload(负载)
第二部分是负载,内容也是一个json对象,它是存放有效信息的地方,它可以存放jwt提供的信息字段,比如:iss(签发者),exp(过期时间戳), sub(面向的用户)等,也可自定义字段。
此部分不建议存放敏感信息,因为此部分可以解码还原原始内容。
例如,一个典型的 Payload 可能如下:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
这个 JSON 被 Base64 编码后形成 JWT 的第二部分。
3.3 Signature(签名)
签名部分是用来验证消息没有被篡改过。
首先将 Header 和 Payload 进行 Base64 编码后组合,编码后使用点(.)连接组成字符串,然后使用 Header 中指定的算法和一个密钥进行签名。
假设使用的是 HMAC SHA256 算法,签名部分的生成方式如下:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
base64UrlEncode(header):jwt令牌的第一部分。
base64UrlEncode(payload):jwt令牌的第二部分。
secret:签名所使用的密钥。
为什么JWT可以防止篡改:
第三部分使用签名算法对第一部分和第二部分的内容进行签名,常用的签名算法是 HS256,常见的还有md5,sha 等,签名算法需要使用密钥进行签名,密钥不对外公开,并且签名是不可逆的,如果第三方更改了内容那么服务器验证签名就会失败,要想保证验证签名正确必须保证内容、密钥与签名前一致。
4. JWT 的使用流程
4.1 用户登录
用户输入凭据(如用户名和密码),服务器验证成功后,会生成一个 JWT,包含用户的一些信息(如用户 ID、角色等),并返回给客户端。
4.2 客户端存储 JWT
客户端通常将 JWT 存储在浏览器的 localStorage
或 sessionStorage
中,或者作为一个 Cookie。
4.3 客户端每次请求时发送 JWT
客户端在每次请求时将 JWT 附带在 HTTP 请求头的 Authorization
字段中,格式如下:
Authorization: Bearer <token>
4.4 服务器验证 JWT
服务器接收到请求后,验证 JWT 的有效性(如签名是否正确,是否过期等)。如果 JWT 有效,服务器就处理请求,否则返回错误状态(如 401 未授权)。
5. 安全性注意事项
- 使用 HTTPS:确保通过 HTTPS 传输 JWT,以防止中间人攻击。
- 使用强大的签名算法:避免使用不安全的签名算法,例如
none
或弱加密算法。 - 定期刷新 JWT:使用短期 JWT,并提供刷新机制,以减少被盗用的风险。
- 保护好密钥:签名 JWT 的密钥必须妥善保管,不应泄露。
6. 使用示例
6.1 创建 JWT
在 Java 中,你可以使用 java-jwt
库来创建和验证 JWT。创建一个简单的 JWT 示例:
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
public class JWTExample {
public static void main(String[] args) {
String token = JWT.create()
.withClaim("userId", 123)
.withClaim("role", "admin")
.sign(Algorithm.HMAC256("secret"));
System.out.println(token);
}
}
6.2 验证 JWT
同样地,你可以使用 java-jwt
库来验证 JWT:
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
public class JWTVerifyExample {
public static void main(String[] args) {
try {
Algorithm algorithm = Algorithm.HMAC256("secret");
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token);
System.out.println("User ID: " + jwt.getClaim("userId").asInt());
} catch (JWTVerificationException exception){
// 无效的签名/声明
exception.printStackTrace();
}
}
}