Java案例如何实现JWT?

wen python案例 7

本文目录导读:

Java案例如何实现JWT?

  1. 添加Maven依赖
  2. 创建JWT工具类
  3. 在Spring Boot中集成JWT(示例)
  4. 测试流程
  5. 常见问题与优化建议

这是一个关于Java实现JWT(JSON Web Token)的完整案例教程,我将从依赖引入、核心工具类、生成与验证Token,到实际在Spring Boot项目中的应用,一步步为你说明。

添加Maven依赖

在你的 pom.xml 中添加JWT相关的依赖,最常用的是 jjwt 库。

<dependencies>
    <!-- JWT核心库 -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.12.5</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.12.5</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.12.5</version>
        <scope>runtime</scope>
    </dependency>
    <!-- Spring Boot Web (如果你是用Spring Boot) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

注意jjwt-api 是编译依赖,jjwt-impljjwt-jackson 是运行时依赖(运行和测试时需要)。


创建JWT工具类

这是一个通用的JWT工具类,包含生成Token解析Token验证Token 的核心方法。

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
public class JwtUtil {
    // 1. 签名密钥(生产环境建议使用Base64编码的强密钥,不要硬编码)
    private static final String SECRET = "YourSuperSecretKeyForJWT2024YoursMustBeAtLeast256BitsLong!!";
    // 2. Token有效期(毫秒),这里设为1小时
    private static final long EXPIRATION = 3600_000L;
    // 3. 生成签名密钥对象
    private static SecretKey getSigningKey() {
        return Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8));
    }
    // 4. 生成JWT Token
    public static String generateToken(String userId, String username) {
        Date now = new Date();
        Date expirationDate = new Date(now.getTime() + EXPIRATION);
        return Jwts.builder()
                .subject(userId)                // 主题:用户ID
                .claim("username", username)    // 自定义声明
                .issuedAt(now)                  // 签发时间
                .expiration(expirationDate)     // 过期时间
                .signWith(getSigningKey())      // 签名
                .compact();
    }
    // 5. 解析Token,获取Claims(包含所有声明)
    public static Claims parseToken(String token) {
        return Jwts.parser()
                .verifyWith(getSigningKey())    // 验证签名
                .build()
                .parseSignedClaims(token)
                .getPayload();
    }
    // 6. 从Token中获取用户ID(即subject)
    public static String getUserId(String token) {
        return parseToken(token).getSubject();
    }
    // 7. 判断Token是否过期
    public static boolean isTokenExpired(String token) {
        try {
            Claims claims = parseToken(token);
            return claims.getExpiration().before(new Date());
        } catch (ExpiredJwtException e) {
            return true;
        }
    }
    // 8. 验证Token是否有效(未过期且签名正确)
    public static boolean validateToken(String token) {
        try {
            parseToken(token);
            return !isTokenExpired(token);
        } catch (JwtException | IllegalArgumentException e) {
            // 签名无效、格式错误、过期等均视为无效
            return false;
        }
    }
}

关键点说明

  • 签名密钥SECRET 至少需要256位(32字节),否则 jjwt 会报错。
  • 声明(Claims)subject 通常存储用户唯一标识(如用户ID),自定义信息通过 claim() 添加。
  • 验证verifyWith() 方法会自动验证签名是否匹配。

在Spring Boot中集成JWT(示例)

1 用户登录接口(返回Token)

@RestController
@RequestMapping("/api/auth")
public class AuthController {
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest request) {
        // 1. 实际项目中这里应该验证用户名密码(此处简化)
        if (!"admin".equals(request.getUsername()) 
                || !"123456".equals(request.getPassword())) {
            return ResponseEntity.status(401).body("用户名或密码错误");
        }
        // 2. 验证通过,生成JWT Token
        String userId = "1001"; // 假设从数据库获取
        String token = JwtUtil.generateToken(userId, request.getUsername());
        // 3. 返回Token给客户端
        return ResponseEntity.ok(new LoginResponse(token));
    }
}
// 请求体
class LoginRequest {
    private String username;
    private String password;
    // 省略 getter/setter
}
// 响应体
class LoginResponse {
    private String token;
    public LoginResponse(String token) { this.token = token; }
    // 省略 getter/setter
}

2 添加JWT拦截器(验证Token)

创建一个过滤器,拦截非登录请求并验证Token。

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) 
            throws ServletException, IOException {
        // 1. 放行登录接口
        if (request.getRequestURI().equals("/api/auth/login")) {
            filterChain.doFilter(request, response);
            return;
        }
        // 2. 从请求头获取Token(格式:Bearer <token>)
        String authHeader = request.getHeader("Authorization");
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            sendError(response, "缺少Token");
            return;
        }
        String token = authHeader.substring(7); // 去掉"Bearer "
        // 3. 验证Token
        if (!JwtUtil.validateToken(token)) {
            sendError(response, "Token无效或已过期");
            return;
        }
        // 4. Token有效,将用户信息存入请求上下文(后续Controller可用)
        String userId = JwtUtil.getUserId(token);
        request.setAttribute("userId", userId);
        // 5. 继续执行后续过滤器链
        filterChain.doFilter(request, response);
    }
    private void sendError(HttpServletResponse response, String message) 
            throws IOException {
        response.setStatus(401);
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write("{\"error\":\"" + message + "\"}");
    }
}

3 注册过滤器(Spring Boot配置)

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private JwtAuthenticationFilter jwtFilter;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtFilter)
                .addPathPatterns("/api/**")  // 拦截所有/api下的请求
                .excludePathPatterns("/api/auth/**"); // 排除登录
    }
}

测试流程

1 获取Token

curl -X POST http://localhost:8080/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"123456"}'

响应:

{
  "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMDAxIiwidXNlcm5hbWUiOiJhZG1pbiIsImlhdCI6MTcwMDAwMDAwMCwiZXhwIjoxNzAwMDAzNjAwfQ.xxxxxx"
}

2 携带Token访问受保护接口

curl http://localhost:8080/api/users/profile \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."

常见问题与优化建议

问题 解决方案
密钥硬编码不安全 将密钥放在 application.yml 中,通过 @Value 注入
Token无法刷新 实现刷新Token机制(参考OAuth2,返回access_token + refresh_token)
Token在黑名单中 使用Redis存储已注销的Token(如在用户登出时)
不支持无状态 JWT本身是无状态的,但黑名单方案会引入状态,根据需求取舍

优化后的密钥配置(application.yml)

jwt:
  secret: YourSuperSecretKeyForJWT2024YoursMustBeAtLeast256BitsLong!!
  expiration: 3600000  # 毫秒

然后在工具类中使用 @Value 注入:

@Component
public class JwtUtil {
    @Value("${jwt.secret}")
    private String secret;
    @Value("${jwt.expiration}")
    private long expiration;
    // ... 其余代码改为非静态方法
}

通过以上案例,你实现了:

  1. 生成JWT:包含用户标识和自定义声明,设置过期时间。
  2. 验证JWT:检查签名完整性、是否过期。
  3. 集成Spring Boot:通过过滤器自动验证每个API请求的Token。
  4. 安全实践:强密钥、统一错误返回、可配置化密钥。

这个结构可以直接应用于你的项目,只需根据业务调整用户信息的内容和验证逻辑即可。

抱歉,评论功能暂时关闭!