JWT和拦截器使用【附Maven中操作步骤】 - 指南

JWT

定义

JSON Web Token,一种开放标准,用于在各方之间安全地传输信息,通常用于身份认证和信息交换。

结构

三部分

header.payload.signature
  • header 包含令牌类型和使用的签名算法
  • payload 包含声明,即要传输的数据,比如用户ID、角色、过期时间等
  • signature 用于验证消息未被篡改,由 Header+Payload+密钥 通过指定算法生成

用途

  • 用户登录后,服务器生成一个 JWT 返回给客户端。
  • 客户端在后续请求的 Authorization 头中携带该JWT。
  • 服务器验证JWT的签名和有效期,从而确认用户身份,无需每次都查询数据库。

优点

  • 无状态(stateless) 服务端不需要保存会话信息
  • 可跨域使用
  • 自包含 Payload 中可携带必要信息

拦截器

定义

拦截器是一种中间件机制,用于在请求到达目标处理函数(如控制器方法) 之前或之后执行通用逻辑。

常见功能

  • 身份验证
  • 日志记录
  • 请求/响应修改
  • 性能监控
  • 错误统一处理

典型使用场景

JWT和拦截器的使用典型场景是:

  1. 用户登录后获得JWT;
  2. 后续每个请求都带上JWT
  3. 服务器的拦截器在请求进入业务逻辑前
    - 检验是否存在 JWT
    - 验证JWT的签名和有效期
    - 如果有效,解析用户信息并附加到请求上下文种
    - 如果无效,直接返回 401 错误,阻止请求继续
    业务代码无需重复处理认证逻辑。

操作步骤

添加依赖

maven的pom.xml中添加依赖,添加后出现标红,需要reload,刷新Maven

<!-- JWT 工具包 -->
  <dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.9.1</version>
  </dependency>
  <!-- XML 解析器 (JDK 11+ 需要手动引入) -->
    <dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.1</version>
    </dependency>

创建 JWT 工具类

后端工具类(一般是utils目录),创建 JwtUtils.java

分为几个步骤:

  1. 设置密钥
  2. 过期时间
  3. 生成 token
  4. 解析 token,获取 Claims
package com.student.devflow.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class JwtUtils {
// 密钥 (真实项目中应该配置在 yml 里,这里为了简单写死)
private static final String SECRET = "123456";
// 过期时间 24小时
private static final long EXPIRATION = 86400000;
// 生成 Token
public static String generateToken(Long userId, String username) {
return Jwts.builder()
.setSubject(username)
.claim("userId", userId)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
.signWith(SignatureAlgorithm.HS256, SECRET)
.compact();
}
// 解析 Token 获取 Claims
public static Claims getClaimsByToken(String token) {
try {
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
return null; // 解析失败或过期
}
}
}

创建登录拦截器

要拦截所有的请求,检查Header中有没有token。
代码位置:
后端/config/xxx.java

package com.student.devflow.config;
import com.student.devflow.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 放行 OPTIONS 请求 (跨域预检)
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
return true;
}
// 1. 从 Header 获取 Token
String token = request.getHeader("Authorization");
// 2. 校验 Token
if (token != null && !token.isEmpty()) {
Claims claims = JwtUtils.getClaimsByToken(token);
if (claims != null) {
// 登录成功,放行
// 可以把 userId 存入 request,方便 Controller 使用
request.setAttribute("userId", claims.get("userId"));
return true;
}
}
// 3. 失败,返回 401
response.setStatus(401);
response.getWriter().write("{\"code\": 401, \"msg\": \"No Permission\"}");
return false;
}
}
代码细节
  1. 判断条件
token != null && !token.isEmpty()

一种防御性编程的经典写法,安全地判断一个字符串是否“真正有内容”。

token != null判断token是否为null。
如果前端根本没传 Authorization头,request.getHeader("Authorization") 会返回 null
如果直接对 null调用 isEmpty(),会抛出空指针异常。

!token.isEmpty()
在确认 token不是null的前提下,在判断它是否为空字符串。
有些客户端传了Header,但它是空的。

  1. Claims claims = JwtUtils.getClaimsByToken(token);
    是 JWT 的 payload 部分的Java对象表示,本质上是键值对,存储了签发Token时放入的所有信息。
    JWT 的结构是
Header.PayloadSignature

其中 ````Payload```是JSON格式:

{
"sub": "alice",
"userId": 1001,
"iat": 1712345678,
"exp": 1712432078
}

调用

Claims claims = JwtUtils.getClaimsByToken(token);

时,JwtUtils内部使用Jwts.parser().parseClaimsJws(token).getBody(),会验证签名+检查过期
如果成功,则会把 payload 解析成一个 Claims对象。
Claims继承自Map<String, Object>,可以像操作Map一样操作它。比如调用get方法。

补充知识

中间件

  1. 在请求到达最终处理逻辑之前(或之后)执行的函数,用于处理通用任务,比如日志记录、身份验证、数据解析、错误处理等。
  2. 特点
    • 可以访问请求对象、响应对象,以及下一个中间件。
    • 可以修改请求或响应。
    • 可以决定是否将请求继续传递给下一个中间件或最终处理函数。
    • 可以提前终止请求(比如返回401未授权错误)
      在spring中,叫Filter或Interceptor

目标处理函数

  1. 通常指真正处理某个 HTTP 请求业务逻辑的函数,也就是请求流程的终点。
  2. 比如Express的路由处理函数,Spring Controller方法,NestJS控制器的方法等。

控制器方法

  1. MVC架构或现代Web框架中,控制器是专门负责处理HTTP请求的类,控制器方法是类中的具体方法,每个方法对应一个路由(URL+HTTP方法)。
  2. 作用:1. 接收请求参数;2. 调用业务逻辑;3. 返回响应。

典型的 HTTP 请求处理顺序

[客户端请求]
     ↓
[全局中间件] → [路由中间件] → [控制器方法(即目标处理函数)]
     ↓
[响应返回]

签名密钥

用于JWT的签名与验签,核心目的是确保JWT的完整性和真实性。
包括令牌未被篡改、令牌确实由可信服务器签发。

如果没有签名或密钥泄露,攻击者可以伪造任意用户身份,如修改userId为管理员ID,服务器无法判断令牌是否可信。

密钥在JWT生命周期中的使用步骤
  1. 生成JWT时,用密钥进行签名。
  2. 服务器将 Header 和 Payload 拼接成字符串(Base64Url 编码后)。使用 HMAC-SHA256 算法 + SECRET 密钥 对这个字符串进行加密,生成 Signature。(只有知道SECRET的人,才能生成合法的签名)
  3. 验证JWT时,用密钥重新计算并比对签名。
  4. 服务器收到JWT,从token中提取headerpayload,用同样的算法和相同的SECRET,重新计算签名,
expectedSignature = HMAC-SHA256(a + "." + b, SECRET)
  1. 将计算出的expectedSignature与token中的原始签名进行比对,如果一致,则令牌合法,未被篡改,如果不一致,则是令牌被伪造或密钥错误,抛出SignatureException
图解流程
[服务器生成 JWT]
       ↓
Header + Payload + 【SECRET】 → 计算 Signature → 组成 token
       ↓
[客户端保存 token,每次请求携带]
       ↓
[服务器收到 token]
       ↓
用【SECRET】重新计算 Signature
       ↓
比对计算值 vs token 中的 Signature
       ↓
一致? → 信任数据(如 userId) → 允许访问
不一致? → 拒绝请求(401 Unauthorized)
posted on 2026-01-13 22:25  ljbguanli  阅读(14)  评论(0)    收藏  举报