深入理解 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 数据格式的自然属性,也让代码更加易于维护和扩展。

相关推荐
徐小黑ACG2 分钟前
GO语言 单元测试
开发语言·后端·golang·单元测试
ShooterJ6 分钟前
API接口开发指南
后端
天天扭码8 分钟前
手把手从零教你用Node.js玩转AI生成:从课设到社团招新的实用指南
javascript·后端·node.js
程序员爱钓鱼11 分钟前
Go 语言实用工具:如何高效解压 ZIP 文件
前端·后端·go
编程乐趣12 分钟前
ZLinq:意在替代Linq的高性能.Net开源库
后端
Truism213 分钟前
MySQL SELECT语句执行顺序
后端
General080513 分钟前
长连接的应用
后端
天天扭码28 分钟前
用Node.js调用DALL-E 3生成图片:程序员的创意革命
javascript·后端·node.js
橘猫云计算机设计29 分钟前
基于Spring Boot 的在线教育系统(源码+lw+部署文档+讲解),源码可白嫖!
数据库·spring boot·后端·数据分析·django·毕业设计