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

简介: JWT(JSON Web Token)中 Claims 是存储用户或业务信息的关键部分。JJWT 库通过 `setClaims` 方法支持传入 `Map<String, Object>` 或 `Claims` 对象,确保灵活性与高效性。本文从源码角度剖析 JWT Claims 的设计思想,介绍三种实现方式:使用 `HashMap`、`DefaultClaims` 和逐个添加声明,并分析其合理性。这种基于 Map 的设计符合 JSON 格式特性,易于扩展且性能优越,为开发者提供灵活的选择,满足不同业务需求。

theme: cyanosis

在使用 JWT(JSON Web Token)时,我们常常需要在 token 中存储一些用户或业务相关的信息,这些信息被称为 claims。从源码的角度来看,JJWT 库设计了一系列方法来设置 claims,它要求传入一个实现了 Map 接口的数据结构。这篇博客将带你从源码出发,详细讲解 JWT Claims 的设计思想、各种实现方式以及它们各自的合理性,并附上实际的代码示例。

image.png

JWT Claims 的设计要求

在 JJWT 库中,构建 JWT 的核心接口是 JwtBuilder。在这个接口中,我们看到与 claims 相关的方法有两个重载版本:

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 方法。这种方式简单直观,适用于声明较少或简单的场景。

代码示例:

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 的高效性能。

代码示例:

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 的步骤,代码更加简洁。

代码示例:

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

相关文章
最新jsonwebtoken-jjwt 0.12.3 基本使用
最新jsonwebtoken-jjwt 0.12.3 基本使用
3820 1
|
存储 消息中间件 负载均衡
深入理解RocketMQ广播消费
这篇文章我们聊聊广播消费,因为广播消费在某些场景下真的有奇效。笔者会从基础概念、实现机制、实战案例、注意事项四个方面一一展开,希望能帮助到大家。
深入理解RocketMQ广播消费
|
8月前
|
Java Spring
jwt解析方法找不到(Cannot resolve method ‘parseClaimsJws‘ in ‘JwtParserBuilder‘ )
解决Cannot resolve method ‘parseClaimsJws‘ in ‘JwtParserBuilder‘
|
缓存 安全 NoSQL
Spring Cloud实战 | 第六篇:Spring Cloud Gateway+ Spring Security OAuth2 + JWT实现微服务统一认证鉴权
Spring Cloud实战 | 第六篇:Spring Cloud Gateway+ Spring Security OAuth2 + JWT实现微服务统一认证鉴权
Spring Cloud实战 | 第六篇:Spring Cloud Gateway+ Spring Security OAuth2 + JWT实现微服务统一认证鉴权
|
10月前
|
安全 Java API
深入解析 Spring Security 配置中的 CSRF 启用与 requestMatchers 报错问题
本文深入解析了Spring Security配置中CSRF启用与`requestMatchers`报错的常见问题。针对CSRF,指出默认已启用,无需调用`enable()`,只需移除`disable()`即可恢复。对于`requestMatchers`多路径匹配报错,分析了Spring Security 6.x中方法签名的变化,并提供了三种解决方案:分次调用、自定义匹配器及降级使用`antMatchers()`。最后提醒开发者关注版本兼容性,确保升级平稳过渡。
1181 2
|
JSON 安全 算法
Spring Boot 应用如何实现 JWT 认证?
Spring Boot 应用如何实现 JWT 认证?
1408 8
|
存储 安全 Java
“Spring Security 中的 Principal 是什么?
【8月更文挑战第21天】
1527 0
|
监控 前端开发 安全
C#一分钟浅谈:文件上传与下载功能实现
【10月更文挑战第2天】在Web应用开发中,文件的上传与下载是常见需求。本文从基础入手,详细讲解如何在C#环境下实现文件上传与下载。首先介绍前端表单设计及后端接收保存方法,使用`&lt;input type=&quot;file&quot;&gt;`与`IFormFile`接口;接着探讨错误处理与优化策略,如安全性验证和路径管理;最后讲解文件下载的基本步骤,包括确定文件位置、设置响应头及发送文件流。此外,还提供了进阶技巧,如并发处理、大文件分块上传及进度监控,帮助开发者构建更健壮的应用系统。
876 16
|
前端开发 Java Spring
SpringBoot通过拦截器和JWT令牌实现登录验证
该文介绍了JWT工具类、匿名访问注解、JWT验证拦截器的实现以及拦截器注册。使用`java-jwt`库生成和验证JWT,JwtUtil类包含generateToken和verifyToken方法。自定义注解`@AllowAnon`允许接口匿名访问。JwtInterceptor在Spring MVC中拦截请求,检查JWT令牌有效性。InterceptorConfig配置拦截器,注册并设定拦截与排除规则。UserController示例展示了注册、登录(允许匿名)和需要验证的用户详情接口。
2456 1