深入理解 JWT 中 Claims 的设计及其合理性

在使用 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 最终都是以键值对的形式存在。这种设计有以下几个优点:

  1. 灵活性高
    不论你使用哪个实现,只要它实现了 Map 接口,JWT 库都能通过遍历来序列化所有的 claims。你可以选择 Java 自带的 HashMap,也可以选择库中提供的 DefaultClaims
  2. 高效性能
    使用 HashMap 或类似的 Map 实现,其插入和查找操作都非常迅速,这在生成和解析 JWT 时能够提供足够的性能保证。
  3. 语义清晰
    通过使用 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);

这种设计的合理性

从源码和设计角度来看,这样的设计是非常合理的,原因有以下几点:

  1. 符合 Map 接口标准
    由于 JWT 的 claims 实际上就是一组键值对数据,使用 Map 来存储它们符合直观的编程习惯,也与 JSON 的数据格式天然契合。
  2. 易于扩展
    开发者可以根据需求选择不同的实现方式(例如使用 HashMapDefaultClaims),也可以灵活地通过 claim(key, value) 方法添加单个属性。这种扩展性满足了各种复杂业务场景的需求。
  3. 解耦合
    JWT 库只依赖于 Map 接口,而不关心具体的实现细节。这样无论未来 Map 的实现有何种变化,或者你需要替换成其他自定义实现,代码都可以很容易适配。
  4. 性能考虑
    选择使用 HashMap 或 DefaultClaims 这样的高效数据结构,可以确保 JWT 构建和解析的过程中不会因为数据结构性能问题而成为瓶颈。

总结

JJWT 库通过要求传入实现了 Map 接口的数据结构来设置 JWT 中的 claims,实现了灵活、高效且语义明确的设计。开发者既可以选择直接传入 HashMap,也可以使用库中提供的 DefaultClaims,或者直接通过单个声明添加方法设置 claims。这些设计不仅符合 JSON 数据格式的自然属性,也让代码更加易于维护和扩展。

相关推荐
命运石之门的选择5 小时前
Flink和CheckPoint简单了解
后端
Java水解5 小时前
Python开发从入门到精通:Web框架Django实战
后端·python
回家路上绕了弯6 小时前
OpenClaw 本地 AI 智能体全解析
后端·agent
belhomme6 小时前
(面试题)Netty 线程模型
java·面试·netty
我爱娃哈哈7 小时前
Spring Cloud Gateway + 请求聚合(GraphQL-like):一次调用合并多个微服务响应
后端
用户298698530147 小时前
C#:三行代码,给 Word 文档的文本框“一键清空”
后端·c#·.net
血小溅7 小时前
Claude Code Superpowers 插件基础教程
后端
树獭叔叔7 小时前
OpenClaw Agents 系统:多代理架构与智能编排的完整技术解析
后端·aigc·openai
蝎子莱莱爱打怪8 小时前
ESXi 强制断电后恢复CentOS7虚拟机避坑指南:解决重复注册&目录清理难题
linux·后端·程序员
ConardLi9 小时前
OpenClaw 完全指南:这可能是全网最新最全的系统化教程了!
前端·人工智能·后端