吃透若依 JWT:从配置到登录鉴权完整流程

 你想了解若依(RuoYi)框架中 JWT 的相关知识,我会从它的作用、核心实现、使用方式等方面为你详细讲解,让你清楚若依框架是如何运用 JWT 来处理身份认证的。

一、JWT 基础与若依框架中的定位

JWT(JSON Web Token)是一种基于 JSON 的轻量级身份认证令牌,在若依框架中,它主要用于前后端分离架构下的无状态身份认证—— 替代传统的 Session 认证,避免服务端存储用户会话信息,同时支持分布式部署(多服务器节点可通过 JWT 直接验证用户身份)。

简单来说,若依框架的 JWT 核心作用是:

  1. 用户登录成功后,生成包含用户信息的 JWT 令牌返回给前端;
  2. 前端后续请求携带该令牌,后端通过验证令牌的有效性(是否过期、是否被篡改)来确认用户身份;
  3. 无需依赖 Session,实现服务端无状态化。

二、若依框架中 JWT 的核心实现

若依框架(以分离版 RuoYi-Vue 为例)的 JWT 核心代码主要在 ruoyi-common 模块的 security 包下,以下是关键部分的拆解和说明:

1. JWT 配置类(JwtConfig)

首先框架会配置 JWT 的核心参数,比如密钥、过期时间等,通常通过配置文件(application.yml)定义:

# 若依框架 JWT 配置示例
ruoyi:
  jwt:
    # 加密密钥(建议自定义,避免默认值)
    secret: abcdefghijklmnopqrstuvwxyz0123456789abcdef
    # 令牌过期时间(单位:秒),默认 2 小时
    expireTime: 7200

对应的配置类(简化版):

@Component
@ConfigurationProperties(prefix = "ruoyi.jwt")
public class JwtConfig {
    // JWT 加密密钥
    private String secret;
    // 令牌过期时间(秒)
    private long expireTime;

    // getter/setter 省略
}

2. JWT 工具类(JwtTokenUtil)

这是核心工具类,负责生成令牌、验证令牌、解析令牌中的用户信息,若依框架的实现如下(关键方法):

@Component
public class JwtTokenUtil {
    @Autowired
    private JwtConfig jwtConfig;

    /**
     * 生成 JWT 令牌(登录成功后调用)
     * @param username 用户名(核心身份信息)
     * @return JWT 令牌
     */
    public String generateToken(String username) {
        // 过期时间 = 当前时间 + 配置的过期时长
        Date expireDate = new Date(System.currentTimeMillis() + jwtConfig.getExpireTime() * 1000);
        return Jwts.builder()
                // 主题 - 存放用户名
                .setSubject(username)
                // 签发时间
                .setIssuedAt(new Date())
                // 过期时间
                .setExpiration(expireDate)
                // 签名算法 + 密钥(防止令牌被篡改)
                .signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret())
                .compact();
    }

    /**
     * 验证令牌是否有效(未过期 + 签名正确)
     * @param token JWT 令牌
     * @return 有效返回 true,否则 false
     */
    public boolean validateToken(String token) {
        try {
            // 解析令牌(自动验证签名和过期时间)
            Jwts.parser().setSigningKey(jwtConfig.getSecret()).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            // 过期、签名错误、令牌格式错误都会抛出异常
            return false;
        }
    }

    /**
     * 从令牌中解析出用户名
     * @param token JWT 令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(jwtConfig.getSecret())
                .parseClaimsJws(token)
                .getBody();
        return claims.getSubject();
    }
}

3. JWT 认证过滤器(JwtAuthenticationFilter)

若依框架通过自定义过滤器拦截所有请求,提取并验证 JWT 令牌,核心逻辑:

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        // 1. 从请求头中提取令牌(若依默认头为 Authorization,格式:Bearer {token})
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String token = authHeader.substring(7); // 截取 Bearer 后的令牌部分
            // 2. 验证令牌有效性
            if (jwtTokenUtil.validateToken(token)) {
                // 3. 解析用户名,加载用户信息
                String username = jwtTokenUtil.getUsernameFromToken(token);
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                // 4. 将用户信息存入 SecurityContext,供后续权限校验使用
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        // 继续执行过滤器链
        chain.doFilter(request, response);
    }
}

4. 登录接口集成 JWT

用户登录成功后,框架会调用 JwtTokenUtil 生成令牌并返回:

@RestController
@RequestMapping("/login")
public class LoginController {
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private AuthenticationManager authenticationManager;

    @PostMapping
    public AjaxResult login(String username, String password) {
        // 1. 验证用户名密码(Spring Security 认证)
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(username, password));
        SecurityContextHolder.getContext().setAuthentication(authentication);
        
        // 2. 生成 JWT 令牌
        String token = jwtTokenUtil.generateToken(username);
        
        // 3. 返回令牌给前端
        return AjaxResult.success().put("token", token);
    }
}

三、若依框架中 JWT 的使用流程

  1. 前端:用户输入账号密码登录,后端返回 JWT 令牌,前端将令牌存储在 localStorage/sessionStorage 中;
  2. 前端请求:后续所有需要认证的请求,在请求头 Authorization 中携带 Bearer {token}
  3. 后端验证:JWT 过滤器拦截请求,提取令牌并验证有效性,有效则解析用户信息,完成身份认证;
  4. 权限校验:认证通过后,框架再通过 Spring Security 校验用户的接口访问权限。

四、若依 JWT 的扩展与注意事项

  1. 令牌刷新:若依默认没有内置刷新逻辑,实际项目中可在令牌快过期时,前端调用刷新接口生成新令牌;
  2. 密钥安全:生产环境务必修改默认密钥(jwt.secret),避免被破解;
  3. 过期时间:可根据业务调整过期时间(如移动端可设更长,管理端设较短);
  4. 注销登录:JWT 本身无法主动作废,若依通常结合 Redis 存储 “黑名单令牌”,注销时将令牌存入 Redis,验证时先检查是否在黑名单。

总结

  1. 若依框架的 JWT 核心作用是实现前后端分离下的无状态身份认证,替代传统 Session;
  2. 核心组件包括:配置类(JwtConfig)、工具类(JwtTokenUtil,负责生成 / 验证 / 解析令牌)、过滤器(JwtAuthenticationFilter,拦截请求验证令牌);
  3. 核心流程:登录生成令牌 → 前端携带令牌请求 → 后端过滤器验证令牌 → 完成身份认证。
升级后的源码下载:

https://gitee.com/ruoyieleadmin/ruoyi-ele-admin