若依框架整合JWT实现前后端分离登录验证

2025-06发布2次浏览

在现代Web开发中,前后端分离架构已经成为主流。若依框架(RuoYi)作为一个基于Spring Boot和Spring Cloud的快速开发平台,提供了丰富的功能模块和良好的扩展性。通过整合JWT(JSON Web Token),可以实现更加安全、高效的登录验证机制。

下面我们将详细解析如何在若依框架中整合JWT实现前后端分离的登录验证。


一、JWT简介

JWT是一种用于在网络应用环境间传递声明的开放标准(RFC 7519)。它由三部分组成:Header(头部)、Payload(载荷)和Signature(签名)。JWT的主要特点包括:

  • 无状态:服务器不需要存储会话信息,所有必要的信息都包含在令牌中。
  • 跨域支持:JWT可以在不同的域名之间传递,适合前后端分离的应用场景。
  • 安全性:通过签名确保数据的完整性。

一个典型的JWT结构如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

二、若依框架整合JWT的步骤

1. 引入依赖

pom.xml中添加JWT相关的依赖:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2. 配置JWT工具类

创建一个工具类用于生成和解析JWT令牌:

import io.jsonwebtoken.*;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtTokenUtil {

    private static final String SECRET_KEY = "yourSecretKey"; // 密钥
    private static final long EXPIRATION_TIME = 86400000; // 过期时间(毫秒)

    // 生成JWT
    public String generateToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, SECRET_KEY)
                .compact();
    }

    // 解析JWT
    public String getUsernameFromToken(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    // 验证JWT是否有效
    public Boolean validateToken(String token, String username) {
        final String extractedUsername = getUsernameFromToken(token);
        return (extractedUsername.equals(username) && !isTokenExpired(token));
    }

    // 提取JWT中的声明
    private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    // 检查JWT是否过期
    private Boolean isTokenExpired(String token) {
        return extractClaim(token, Claims::getExpiration).before(new Date());
    }

    // 提取所有声明
    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
    }
}

3. 修改登录逻辑

在用户登录成功后,生成JWT并返回给前端:

@RestController
@RequestMapping("/auth")
public class AuthController {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
        String username = loginRequest.getUsername();
        String password = loginRequest.getPassword();

        // 验证用户名和密码(此处假设已实现认证逻辑)
        if (!"admin".equals(username) || !"password".equals(password)) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
        }

        // 生成JWT
        String token = jwtTokenUtil.generateToken(username);

        // 返回JWT给前端
        return ResponseEntity.ok(new HashMap<String, String>() {{
            put("token", token);
        }});
    }
}

4. 添加拦截器验证JWT

为了确保每次请求都携带有效的JWT,可以添加一个拦截器进行验证:

@Component
public class JwtRequestFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        final String authorizationHeader = request.getHeader("Authorization");

        String username = null;
        String jwt = null;

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            jwt = authorizationHeader.substring(7);
            username = jwtTokenUtil.getUsernameFromToken(jwt);
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            if (jwtTokenUtil.validateToken(jwt, username)) {
                UsernamePasswordAuthenticationToken authenticationToken =
                        new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }

        chain.doFilter(request, response);
    }
}

5. 前端使用JWT

前端在登录成功后保存JWT,并在每次请求时将JWT放入Authorization头中:

function login(username, password) {
    fetch('/auth/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password })
    })
    .then(response => response.json())
    .then(data => {
        localStorage.setItem('token', data.token); // 保存JWT
    });
}

function callApi() {
    const token = localStorage.getItem('token');
    fetch('/api/some-endpoint', {
        method: 'GET',
        headers: { 'Authorization': `Bearer ${token}` }
    })
    .then(response => response.json())
    .then(data => console.log(data));
}

三、流程图

以下是整个JWT登录验证的流程图:

sequenceDiagram
    participant 用户 as User
    participant 前端 as Frontend
    participant 后端 as Backend
    participant 数据库 as Database

    Note over 用户,Frontend: 用户输入账号密码
    Frontend->>Backend: POST /auth/login {username, password}
    Backend->>Database: 验证用户名和密码
    Database-->>Backend: 返回验证结果
    Backend->>Frontend: 返回JWT
    Frontend->>用户: 显示登录成功

    Note over 用户,Frontend: 用户发起API请求
    Frontend->>Backend: GET /api/... Authorization: Bearer 
    Backend->>Backend: 验证JWT有效性
    alt JWT有效
        Backend->>Database: 查询数据
        Database-->>Backend: 返回数据
        Backend-->>Frontend: 返回响应数据
    else JWT无效
        Backend-->>Frontend: 返回401 Unauthorized
    end

四、总结

通过以上步骤,我们成功地在若依框架中整合了JWT,实现了前后端分离的登录验证机制。这种方式不仅简化了会话管理,还提高了系统的可扩展性和安全性。