老生常谈的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 从入门到精通,赶紧动手试试吧!有什么问题,欢迎留言聊聊!

相关推荐
刘鹏3784 分钟前
深入浅出Java中的CAS:原理、源码与实战应用
后端
Lx3529 分钟前
《从头开始学java,一天一个知识点》之:循环结构:for与while循环的使用场景
java·后端
fliter9 分钟前
RKE1、K3S、RKE2 三大 Kubernetes 发行版的比较
后端
aloha_10 分钟前
mysql 某个客户端主机在短时间内发起了大量失败的连接请求时
后端
程序员爱钓鱼11 分钟前
Go 语言高效连接 SQL Server(MSSQL)数据库实战指南
后端·go·sql server
xjz184212 分钟前
Java AQS(AbstractQueuedSynchronizer)实现原理详解
后端
Victor35612 分钟前
Zookeeper(97)如何在Zookeeper中实现分布式协调?
后端
至暗时刻darkest12 分钟前
go mod文件 项目版本管理
开发语言·后端·golang
程序员爱钓鱼12 分钟前
Go 语言高效连接 MySQL 数据库:从入门到实战
后端·mysql·go
陈随易13 分钟前
前端之虎:现代前端开发必备依赖(第3期)
前端·后端·程序员