老生常谈的JWT:你真懂HSA和RSA么?谈谈对称加密和非对称加密

大家好,今天我们来聊聊如何在 Spring Boot 中集成 JWT(JSON Web Token)。我会从最基础的配置讲起,一步步带你理解 JWT 的加密方法、生成过程和验证方式。我们会从一个简单的实现开始,发现其中的问题,然后逐步优化,逼近如今最主流的方案。废话不多说,直接开整!


1. Spring Boot 集成 JWT 需要的 POM 依赖

要在 Spring Boot 项目中使用 JWT,我们得先把必要的依赖加到 pom.xml 里。JWT 是个用来安全传输信息的标准,特别适合做身份验证。下面是需要的依赖:

xml 复制代码
<!-- JWT 核心 API -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<!-- JWT 实现 -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<!-- JSON 处理 -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

这些依赖是干嘛的?

  • jjwt-api:提供 JWT 的核心接口,比如生成和解析 token。
  • jjwt-impl:具体的实现逻辑,runtime 作用域表示运行时才需要。
  • jjwt-jackson:处理 JSON,因为 JWT 的数据部分是 JSON 格式的,靠它来序列化和反序列化。

有了这些依赖,我们就可以开始玩转 JWT 了!


2. JWT 的加密方法:有哪些?怎么选?

JWT 的安全性主要靠签名,签名用到的加密方法直接决定了 token 的可靠程度。常见的加密方法有这么几种:

常见的加密方法

  1. HS256(HMAC with SHA-256)

    • 类型:对称加密
    • 特点:用同一个密钥签名和验证,简单高效。
    • 适用场景:单服务系统,密钥管理不复杂。
  2. RS256(RSA with SHA-256)

    • 类型:非对称加密
    • 特点:私钥签名,公钥验证,安全性高。
    • 适用场景:分布式系统,公钥可以公开分享。
  3. ES256(ECDSA with SHA-256)

    • 类型:非对称加密
    • 特点:用椭圆曲线算法,密钥短,签名快。
    • 适用场景:移动设备或资源受限的场景。

比较一下它们的异同

算法 类型 密钥管理 安全性 性能 用在哪儿
HS256 对称 一个密钥搞定 中等 单服务、小项目
RS256 非对称 私钥签,公钥验 中等 分布式、公开 API
ES256 非对称 私钥签,公钥验 移动端、IoT设备

举个例子帮你记住

想象你是个店老板,要给顾客发"VIP凭证":

  • HS256:你用一个秘密口令写凭证,顾客拿回来你再用这个口令验。简单,但口令得藏好,不能泄露。
  • RS256:你用私人印章盖凭证,顾客拿回来你给个公开的"印章模板"就能验。印章丢不了,别人还能帮你验。
  • ES256:类似 RS256,但你的印章更小巧,盖章更快,适合给手机用户发凭证。

3. 生成 JWT:结构和代码

JWT 长啥样?它由三部分组成,用点号分隔:

css 复制代码
Header.Payload.Signature

JWT 的通用结构

  • Header :描述 token 的类型和加密算法,比如:

    json 复制代码
    {
      "alg": "HS256",
      "typ": "JWT"
    }
  • Payload :放实际数据(声明),比如用户信息:

    json 复制代码
    {
      "sub": "10086",
      "name": "小明",
      "iat": 1698765432
    }
  • Signature:用密钥对 Header 和 Payload 签名,确保不被篡改。

这三部分会分别被 Base64 编码,然后用点连起来,最后加上签名。

生成 JWT 的代码(用 HS256)

下面用 Java 代码生成一个 token:

java 复制代码
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;

public class JwtDemo {
    public static void main(String[] args) {
        // 生成一个 HS256 的密钥
        Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);

        // 生成 JWT
        String jwt = Jwts.builder()
            .setHeaderParam("typ", "JWT") // Header
            .setSubject("10086")          // Payload: 用户ID
            .setClaim("name", "小明")     // Payload: 自定义声明
            .setIssuedAt(new Date())      // Payload: 发行时间
            .signWith(key)                // Signature: 用密钥签名
            .compact();                   // 合成 token

        System.out.println("生成的 JWT: " + jwt);
    }
}

运行后,你会得到一个长字符串,比如:

复制代码
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMDA4NiIsIm5hbWUiOiLlsbPniYgiLCJpYXQiOjE2OTg3NjU0MzJ9.一串签名

4. 验证 JWT:怎么解密和检查?

验证 JWT 其实不涉及"解密",而是检查签名和声明。签名对了,token 就是真的;声明没问题(比如没过期),就能用。

验证步骤

  1. 解析 Header:看看用的是啥算法。
  2. 检查签名:用密钥验证签名是否匹配。
  3. 检查 Payload:确认声明,比如时间是否过期。

验证代码(用 HS256)

接着上面的例子,验证一下:

java 复制代码
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;

public class JwtDemo {
    public static void main(String[] args) {
        Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
        String jwt = "刚刚生成的那个 token";

        try {
            Claims claims = Jwts.parserBuilder()
                .setSigningKey(key)        // 设置密钥
                .build()
                .parseClaimsJws(jwt)       // 解析 token
                .getBody();                // 拿到 Payload
            System.out.println("用户ID: " + claims.getSubject());
            System.out.println("用户名: " + claims.get("name"));
        } catch (Exception e) {
            System.out.println("token 不合法!");
        }
    }
}

用到的核心 API 是 Jwts.parserBuilder()parseClaimsJws(),它们帮你把 token 拆开并验证签名。


从简单到复杂:优化 JWT 的使用

朴素方案:HS256 单密钥

我们先从最简单的 HS256 开始:

  • 服务器用一个密钥生成 token。
  • 客户端拿着 token 访问,服务器用同一个密钥验证。

问题来了

  1. 密钥咋管? 单服务还好说,但如果有多个服务,得把密钥传过去,容易泄露。
  2. 安全性咋办? 密钥丢了,别人就能伪造 token。

优化方向

  • 换非对称加密:用 RS256,私钥签名,公钥验证,公钥随便发,不怕泄露。
  • 分布式友好:多个服务都能用公钥验 token,不用共享私钥。

主流方案:RS256 非对称加密

改用 RS256:

  • 服务器用私钥生成 token。
  • 客户端访问时,服务器或其他服务用公钥验证。

好处

  • 安全性提升,私钥只留服务器。
  • 分布式系统里,公钥随便分发,验证方便。

代码调整

生成 token 时用 RSA 密钥对:

java 复制代码
import java.security.KeyPair;
import io.jsonwebtoken.security.Keys;

KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256);
String jwt = Jwts.builder()
    .setSubject("10086")
    .signWith(keyPair.getPrivate())
    .compact();

验证时用公钥:

java 复制代码
Claims claims = Jwts.parserBuilder()
    .setSigningKey(keyPair.getPublic())
    .build()
    .parseClaimsJws(jwt)
    .getBody();

再进一步:解决 token 不可撤销

RS256 虽然安全,但 JWT 一旦发出,除非过期,否则没法主动废掉。如果 token 被偷了,麻烦就大了。

优化方案

  • 短命 access token + 长命 refresh token:access token 设个短过期时间(比如 15 分钟),过期后用 refresh token 换新的。
  • 黑名单机制:用户退出或 token 被盗时,把 refresh token 加到黑名单。

主流做法

  • access token:有效期短,负责日常认证。
  • refresh token:有效期长,负责续命。
  • 黑名单:用 Redis 存被废掉的 token,验证时查一下。

总结

在 Spring Boot 里集成 JWT,核心是加好依赖(jjwt-api 等),选对加密方法(HS256、RS256、ES256),然后用代码生成和验证 token。从最简单的 HS256 开始,我们发现密钥管理的问题,进化到 RS256 的非对称加密,适应了分布式需求。再加上 refresh token 和黑名单,就成了现在最靠谱的方案。

希望这篇博客能让你对 JWT 从入门到精通,赶紧动手试试吧!有什么问题,欢迎留言聊聊!

相关推荐
源码云商1 小时前
基于Spring Boot + Vue的母婴商城系统( 前后端分离)
java·spring boot·后端
还听珊瑚海吗5 小时前
基于SpringBoot的抽奖系统测试报告
java·spring boot·后端
你怎么知道我是队长7 小时前
Go语言标识符
后端·golang
sco528211 小时前
SpringBoot 自动装配原理 & 自定义一个 starter
java·spring boot·后端
海风极客12 小时前
《Go小技巧&易错点100例》第三十三篇
开发语言·后端·golang
养军博客12 小时前
Spring boot 简单开发接口
java·spring boot·后端
计算机学姐15 小时前
基于SpringBoot的在线教育管理系统
java·vue.js·spring boot·后端·mysql·spring·mybatis
有梦想的攻城狮15 小时前
spring中的@Value注解详解
java·后端·spring·value注解
编程乐趣16 小时前
基于.Net Core开发的GraphQL开源项目
后端·.netcore·graphql
阿乾之铭17 小时前
Spring Boot 中的重试机制
java·spring boot·后端