在使用 JWT(JSON Web Token)时,我们常常需要在 token 中存储一些用户或业务相关的信息,这些信息被称为 claims。从源码的角度来看,JJWT 库设计了一系列方法来设置 claims,它要求传入一个实现了 Map 接口的数据结构。这篇博客将带你从源码出发,详细讲解 JWT Claims 的设计思想、各种实现方式以及它们各自的合理性,并附上实际的代码示例。
JWT Claims 的设计要求
在 JJWT 库中,构建 JWT 的核心接口是 JwtBuilder
。在这个接口中,我们看到与 claims 相关的方法有两个重载版本:
java
JwtBuilder setClaims(Claims var1);
JwtBuilder setClaims(Map<String, Object> var1);
这意味着你可以传入一个 Claims
对象,也可以传入一个普通的 Map<String, Object>
。实际上,Claims
接口本身就扩展了 Map<String, Object>
,因此从设计上来说,JWT 库期望所有的 claims 最终都是以键值对的形式存在。这种设计有以下几个优点:
- 灵活性高
不论你使用哪个实现,只要它实现了 Map 接口,JWT 库都能通过遍历来序列化所有的 claims。你可以选择 Java 自带的HashMap
,也可以选择库中提供的DefaultClaims
。 - 高效性能
使用 HashMap 或类似的 Map 实现,其插入和查找操作都非常迅速,这在生成和解析 JWT 时能够提供足够的性能保证。 - 语义清晰
通过使用Claims
接口,代码语义上更加明确 ------ 我们传递的是"声明"而不是普通的 Map,从而增强了代码的可读性。
不同方式设置 Claims 的实现
方式一:直接使用 HashMap
直接创建一个 HashMap
来存放所有你想要添加的声明,再一次性传递给 setClaims
方法。这种方式简单直观,适用于声明较少或简单的场景。
代码示例:
java
HashMap<String, Object> claims = new HashMap<>();
claims.put("id", "23");
claims.put("username", "大郎");
String token = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS256, "wolfcode")
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000 * 24))
.compact();
System.out.println(token);
方式二:使用 DefaultClaims
JJWT 库中提供了 DefaultClaims
类,它实现了 Claims
接口。使用 DefaultClaims
可以让代码语义上更贴近"声明"的概念,同时依然享受 Map 的高效性能。
代码示例:
java
DefaultClaims claims = new DefaultClaims();
claims.put("id", "23");
claims.put("username", "大郎");
String token = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS256, "wolfcode")
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000 * 24))
.compact();
System.out.println(token);
方式三:逐个添加声明
对于简单的场景,如果你只需要添加几个声明,也可以直接使用 claim(key, value)
方法逐个设置。这种方式省去了创建 Map 的步骤,代码更加简洁。
代码示例:
java
String token = Jwts.builder()
.claim("id", "23")
.claim("username", "大郎")
.signWith(SignatureAlgorithm.HS256, "wolfcode")
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000 * 24))
.compact();
System.out.println(token);
这种设计的合理性
从源码和设计角度来看,这样的设计是非常合理的,原因有以下几点:
- 符合 Map 接口标准
由于 JWT 的 claims 实际上就是一组键值对数据,使用 Map 来存储它们符合直观的编程习惯,也与 JSON 的数据格式天然契合。 - 易于扩展
开发者可以根据需求选择不同的实现方式(例如使用HashMap
或DefaultClaims
),也可以灵活地通过claim(key, value)
方法添加单个属性。这种扩展性满足了各种复杂业务场景的需求。 - 解耦合
JWT 库只依赖于 Map 接口,而不关心具体的实现细节。这样无论未来 Map 的实现有何种变化,或者你需要替换成其他自定义实现,代码都可以很容易适配。 - 性能考虑
选择使用 HashMap 或 DefaultClaims 这样的高效数据结构,可以确保 JWT 构建和解析的过程中不会因为数据结构性能问题而成为瓶颈。
总结
JJWT 库通过要求传入实现了 Map 接口的数据结构来设置 JWT 中的 claims,实现了灵活、高效且语义明确的设计。开发者既可以选择直接传入 HashMap
,也可以使用库中提供的 DefaultClaims
,或者直接通过单个声明添加方法设置 claims。这些设计不仅符合 JSON 数据格式的自然属性,也让代码更加易于维护和扩展。