news 2026/4/30 11:35:56

Spring Boot项目实战:用JJWT 0.11.5搞定用户登录与API鉴权(附完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot项目实战:用JJWT 0.11.5搞定用户登录与API鉴权(附完整代码)

Spring Boot实战:基于JJWT 0.11.5构建企业级JWT认证体系

在微服务架构盛行的今天,API安全认证已成为系统设计的核心环节。传统基于Session的认证方式在分布式场景下暴露出扩展性差、耦合度高的问题,而JWT(JSON Web Token)凭借其无状态、自包含的特性,成为现代应用认证的首选方案。本文将基于Spring Boot 3.x和JJWT 0.11.5,从零构建一套生产可用的JWT认证体系,涵盖密钥管理、令牌签发、接口鉴权等全流程,并分享在实际项目中的优化经验。

1. 环境准备与基础配置

1.1 项目初始化与依赖管理

使用Spring Initializr创建项目时,需确保选择Spring Boot 3.x版本。JJWT的依赖配置需要特别注意模块化设计:

<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency>

注意:jjwt-impl必须声明为runtime作用域,这是JJWT官方强烈建议的做法,因为其内部实现可能在不通知的情况下发生变化。

1.2 密钥管理策略

生产环境中密钥管理至关重要,推荐采用以下两种方案:

方案类型实现方式适用场景安全等级
对称加密HS256/HS384/HS512内部服务间通信★★★☆
非对称加密RS256/ES256对外开放API★★★★

生成HMAC-SHA256密钥的示例:

SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256); String base64Key = Encoders.BASE64.encode(key.getEncoded());

建议将密钥存储在环境变量或配置中心,避免硬编码在代码中。对于Kubernetes环境,可通过Secret对象注入:

# 生成随机密钥 kubectl create secret generic jwt-secret --from-literal=key=$(openssl rand -base64 32)

2. 核心组件设计与实现

2.1 JWT工具类封装

创建JwtUtils工具类,封装令牌的生成、解析等操作:

public class JwtUtils { private static final SignatureAlgorithm ALGORITHM = SignatureAlgorithm.HS256; private final SecretKey secretKey; private final long expirationMs; public JwtUtils(String base64Key, long expirationMs) { this.secretKey = Keys.hmacShaKeyFor(Decoders.BASE64.decode(base64Key)); this.expirationMs = expirationMs; } public String generateToken(UserDetails user) { return Jwts.builder() .setSubject(user.getUsername()) .claim("roles", user.getAuthorities()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + expirationMs)) .signWith(secretKey, ALGORITHM) .compact(); } public Claims parseToken(String token) { return Jwts.parserBuilder() .setSigningKey(secretKey) .build() .parseClaimsJws(token) .getBody(); } }

2.2 认证过滤器实现

继承OncePerRequestFilter实现JWT认证过滤器:

public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtUtils jwtUtils; private final UserDetailsService userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String header = request.getHeader("Authorization"); if (StringUtils.hasText(header) && header.startsWith("Bearer ")) { String token = header.substring(7); try { Claims claims = jwtUtils.parseToken(token); String username = claims.getSubject(); UserDetails user = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (JwtException e) { response.sendError(HttpStatus.UNAUTHORIZED.value(), "Invalid token"); return; } } chain.doFilter(request, response); } }

3. 进阶功能实现

3.1 令牌刷新机制

实现无感知刷新方案需要客户端和服务端协同工作:

  1. 服务端在签发令牌时同时生成refresh token
  2. refresh token具有更长有效期,存储于Redis等缓存中
  3. 客户端在access token过期时,使用refresh token获取新令牌

Refresh token生成示例:

public String generateRefreshToken(UserDetails user) { return Jwts.builder() .setSubject(user.getUsername()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + refreshExpirationMs)) .signWith(secretKey, ALGORITHM) .compact(); }

3.2 黑名单管理

对于需要提前失效的令牌,可采用Redis黑名单方案:

public void invalidateToken(String token) { Claims claims = parseToken(token); long ttl = claims.getExpiration().getTime() - System.currentTimeMillis(); redisTemplate.opsForValue().set( "jwt:blacklist:" + token, "1", ttl, TimeUnit.MILLISECONDS ); }

在认证过滤器中增加黑名单检查:

if (redisTemplate.hasKey("jwt:blacklist:" + token)) { response.sendError(HttpStatus.UNAUTHORIZED.value(), "Token revoked"); return; }

4. 安全加固与性能优化

4.1 防御措施实施

针对常见JWT攻击手段的防护策略:

  • 令牌泄露:设置合理的有效期,使用HTTPS传输
  • 重放攻击:添加jti(唯一标识)和nonce校验
  • 算法混淆:明确指定签名算法

增强版令牌生成:

public String generateSecureToken(UserDetails user) { return Jwts.builder() .setHeaderParam("alg", ALGORITHM.getValue()) .setHeaderParam("typ", "JWT") .setSubject(user.getUsername()) .claim("roles", user.getAuthorities()) .setId(UUID.randomUUID().toString()) // jti .setIssuedAt(new Date()) .setNotBefore(new Date()) // nbf .setExpiration(new Date(System.currentTimeMillis() + expirationMs)) .signWith(secretKey, ALGORITHM) .compact(); }

4.2 性能调优技巧

  1. 缓存公钥:对于RS256等非对称算法,将公钥缓存起来避免重复解析
  2. 并行验证:对于批量请求,可使用CompletableFuture并行验证
  3. 精简声明:控制payload大小,避免过度膨胀

使用Caffeine缓存公钥示例:

LoadingCache<String, PublicKey> publicKeyCache = Caffeine.newBuilder() .maximumSize(100) .expireAfterWrite(1, TimeUnit.HOURS) .build(kid -> fetchPublicKeyFromKMS(kid));

5. 测试与部署策略

5.1 集成测试方案

使用Spring Security Test和MockMvc编写测试用例:

@SpringBootTest @AutoConfigureMockMvc class AuthControllerTest { @Autowired private MockMvc mockMvc; @Test void shouldReturnTokenWhenLoginSuccess() throws Exception { mockMvc.perform(post("/api/auth/login") .contentType(MediaType.APPLICATION_JSON) .content("{\"username\":\"admin\",\"password\":\"123456\"}")) .andExpect(status().isOk()) .andExpect(jsonPath("$.token").exists()); } @Test void shouldRejectRequestWithInvalidToken() throws Exception { mockMvc.perform(get("/api/users") .header("Authorization", "Bearer invalid.token.here")) .andExpect(status().isUnauthorized()); } }

5.2 生产部署建议

  1. 密钥轮换:建立定期更换密钥的机制
  2. 监控指标:收集JWT验证失败率、过期率等指标
  3. 灾备方案:准备应急密钥对,确保业务连续性

Prometheus监控指标示例:

@Bean MeterRegistryCustomizer<MeterRegistry> jwtMetrics() { return registry -> Counter.builder("jwt.verification") .tag("result", "success") .register(registry); }

在项目实际落地过程中,发现最易出错的环节是令牌过期时间的处理。特别是在跨时区部署时,务必确保所有服务器时间同步。另外,对于高并发系统,建议将JWT验证逻辑下沉到API网关层,既能统一处理认证逻辑,又能减轻业务服务压力。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/30 11:28:54

支付集成工具ovra-pay解析:适配器模式与统一接口设计实践

1. 项目概述&#xff1a;一个面向开发者的支付集成解决方案最近在做一个需要接入支付功能的小项目&#xff0c;找了一圈开源方案&#xff0c;发现了一个挺有意思的库——Ovra-Labs/ovra-pay。乍一看这个名字&#xff0c;可能会觉得有点陌生&#xff0c;但深入研究后&#xff0c…

作者头像 李华
网站建设 2026/4/30 11:26:08

Sunshine游戏串流部署指南:如何构建低延迟自托管云游戏服务器

Sunshine游戏串流部署指南&#xff1a;如何构建低延迟自托管云游戏服务器 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine Sunshine是一款开源的自托管游戏串流服务器&#xff0c;专…

作者头像 李华
网站建设 2026/4/30 11:21:13

LabVIEW数据库事务操作

程序通过 Database Connectivity Toolkit 实现数据库事务管理&#xff0c;核心功能为创建数据表并根据用户选择执行提交&#xff08;Commit&#xff09;或回滚&#xff08;Rollback&#xff09;操作&#xff0c;同时处理数据表已存在的异常场景&#xff0c;确保数据操作的一致性…

作者头像 李华