JWT 工具类设计的“两种境界”:静态工具 vs. Spring Bean ✨

对比这两种 JWT 工具类的设计风格,是一个非常棒的技术分享主题,它能清晰地展示出“配置与代码分离”以及“依赖注入”在现代软件开发中的重要性。


JWT 工具类设计的“两种境界”:静态工具 vs. Spring Bean ⚔️

嘿,各位后端开发者!👋 在构建需要身份认证的系统时,JWT (JSON Web Token) 无疑是我们的首选方案。而在 Java 项目中,我们通常会创建一个 JwtUtilJwtUtils 的工具类来封装 Token 的生成和验证逻辑。

但是,这个小小的工具类,却有两种截然不同的设计“境界”:

  1. 传统静态工具类 (static 方法):简单直接,随处可用。
  2. 现代 Spring Bean (@Component 注入):拥抱框架,灵活可配。

这两种方式都能完成任务,但它们在可维护性、可测试性和安全性上却有着天壤之别。今天,我们就结合具体的代码,深入剖析这两种设计方式的优劣,看看你的项目正处于哪个“境界”,以及如何向更现代、更健壮的设计演进。

🎯 核心差异速览

特征传统静态工具类 (JwtUtils)现代 Spring Bean (JwtUtil)
设计思想面向过程,功能集合面向对象,依赖注入 (DI)
配置管理硬编码在代码中 ❌通过 application.yml 集中管理 ✅
灵活性,修改配置需改代码并重新部署,修改配置只需重启或动态刷新
可测试性困难,静态方法难以模拟 (Mock)简单,可轻松模拟 Bean 的行为 ✅
与框架关系游离于 Spring 体系之外与 Spring 生态无缝集成

📜 境界一:简单直接的静态工具类 (JwtUtils)

这是很多开发者入门时最自然的选择。我们把所有功能方法都声明为 static,方便在任何地方直接通过类名调用。

代码实现 (JwtUtils.java)
package com.productQualification.common.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
// ... 其他 imports ...
import java.util.Calendar;
import java.util.Date;

public class JwtUtils {

    public static String createToken(String userId, String type, String userName) {
        // 1. 有效期被硬编码为 1 天
        Calendar nowTime = Calendar.getInstance();
        nowTime.add(Calendar.DAY_OF_YEAR, 1);
        Date expiresDate = nowTime.getTime();

        return JWT.create().withAudience(userId)
                .withIssuedAt(new Date())
                .withExpiresAt(expiresDate)
                .withClaim("userName", userName)
                .withClaim("type", type)
                // 2. 密钥是根据 userId 和一个硬编码的盐值 "clear" 动态生成的
                .sign(Algorithm.HMAC256(userId + "clear"));
    }

    public static void verifyToken(String token, String secret) throws TokenException {
        // 验证时,也需要用 userId + "clear" 来构造密钥
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret + "clear")).build();
        verifier.verify(token);
        // ... 异常处理 ...
    }
    // ... 其他静态方法 ...
}
调用方式
// 在 Service 中直接调用
String token = JwtUtils.createToken("123", "user", "Alice");
优点与“陷阱”

这种方式的优点是简单。但它的“陷阱”也正源于此:它将易变的信息(配置)和不变的逻辑(代码)耦合在了一起

  • 想改有效期? -> 修改代码,重新编译,重新部署。
  • 想换加密盐值? -> 修改代码,重新编译,重新部署。
  • 想写单元测试? -> 无法模拟 JwtUtils.createToken 的行为,测试变得困难。

✨ 境界二:拥抱框架的 Spring Bean (JwtUtil)

在 Spring Boot 的世界里,我们有更好的选择。通过将工具类声明为一个 @Component,我们可以利用 Spring 强大的依赖注入配置管理能力。

配置文件 (application.yml)

首先,我们将所有易变的信息抽离到配置文件中。

# application.yml
jwt:
  secret: "your-super-secret-key-for-jwt-that-is-long-and-random"
  expiration: 604800000 # 7天的毫秒数
代码实现 (JwtUtil.java)
package com.productQualification.suitselection.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
// ... 其他 imports ...
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;

@Component // 1. 声明为 Spring 组件 (Bean)
public class JwtUtil {

    // 2. 从 application.yml 注入密钥
    @Value("${jwt.secret}")
    private String secret;

    // 3. 从 application.yml 注入过期时间
    @Value("${jwt.expiration}")
    private long expiration;

    // 注意:方法不再是 static 的
    public String createToken(Integer userId, String openId, String userType) {
        Date now = new Date();
        // 4. 使用注入的配置值
        Date expiryDate = new Date(now.getTime() + expiration);
        Algorithm algorithm = Algorithm.HMAC256(secret);

        return JWT.create()
                .withSubject(String.valueOf(userId))
                .withClaim("openId", openId)
                .withClaim("userType", userType)
                .withIssuedAt(now)
                .withExpiresAt(expiryDate)
                .sign(algorithm);
    }
    // ... 其他实例方法 ...
}
调用方式
// 在 Service 中通过 @Autowired 注入
@Service
public class WxAuthService {
    @Autowired
    private JwtUtil jwtUtil; // 注入 Bean

    public WxLoginVO login(...) {
        // ...
        String token = jwtUtil.createToken(...); // 调用实例方法
        // ...
    }
}

流程对比:两种设计下的 Token 生成之旅

境界二:Spring Bean
境界一:静态工具类
读取 application.yml
创建 JwtUtil Bean
并注入配置值 (secret, expiration)
Spring 容器启动
方法使用
已注入的配置值
Service 调用
jwtUtil.createToken()
生成 Token
方法内部
硬编码有效期和盐值
Service 调用
JwtUtils.createToken()
生成 Token

时序图:依赖注入如何工作

Spring容器WxAuthServiceJwtUtil1. 创建 JwtUtil 实例2. 读取 yml, 注入 secret 和 expiration依赖注入阶段3. 创建 WxAuthService 实例4. 将已创建的 JwtUtil 实例注入到 WxAuthService业务调用阶段5. login() 方法中调用 createToken()Spring容器WxAuthServiceJwtUtil

状态图:配置的生命周期

硬编码 (Static)
外部化 (Bean)
"动态刷新配置 (可选)"
Static
配置与代码生命周期绑定
修改需重新编译
Bean
配置独立于代码
修改只需重启或刷新

类图与实体关系图

"注入 (injects)"
"依赖 (depends on)"
WxAuthService
+jwtUtil: JwtUtil
+login()
«Component»
JwtUtil
-secret: string
-expiration: long
+createToken()
«YAML»
ApplicationConfig
+jwt.secret: string
+jwt.expiration: long
APPLICATION_YMLstringjwt_secretPKlongjwt_expirationJWT_UTIL_BEANstringsecretFKlongexpirationSERVICE_BEANstringname配置被注入到

ERD (Entity Relationship Diagram, 实体关系图)

🧠 思维导图总结

在这里插入图片描述

虽然静态工具类在简单的场景下能快速解决问题,但从长远来看,拥抱框架、遵循“配置与代码分离”的原则,才是通往高质量、可维护软件的康庄大道。你的项目,正处于哪个境界呢?不妨检查一下吧!Happy coding! 🚀

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值