SpringBoot项目集成Redis+JWT实现系统登录token校验

原理

  1. 用户登录系统时,后端拿到账号密码进行登录校验(查询数据库),校验通过生成token返回给前端,并放行请求的资源
  2. 后续前端每次请求后端接口时,都在请求头中带上token,后端的全局拦截器拦截到请求,去redis查询缓存的token,找到对应token则放行请求到对应接口方法,否则返回未登录提醒。

pom文件中引入依赖(gradle同样)

xml 复制代码
<!-- Redis 相关依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- JWT 相关依赖 -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.11.2</version>
</dependency>

配置redis

xml 复制代码
# Redis 连接配置
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=your_redis_password

JWT Token 生成校验工具

java 复制代码
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;

@Component
public class JwtTokenUtil {

    private static final String SECRET_KEY = "your_secret_key";
    private static final long EXPIRATION_TIME = 86400000; // 10 days

    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, SECRET_KEY)
                .compact();
    }

    public String getUsernameFromToken(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getSubject();
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

用户登录控制器:

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.HashMap;
import java.util.Map;

@RestController
public class LoginController {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @PostMapping("/login")
    public Map<String, String> login(@RequestBody Map<String, String> request) {
        String username = request.get("username");
        String password = request.get("password");

        // todo 这里可以加入登录逻辑,验证用户名密码
        // 校验通过 生成 Token
        // 校验不通过 给出错误提示用户名密码错误
        String token = jwtTokenUtil.generateToken(username);

        // 缓存 Token 到 Redis
        redisTemplate.opsForValue().set(username, token);
        // 设置过期时间
        redisTemplate.expire(username, jwtTokenUtil.EXPIRATION_TIME, TimeUnit.MILLISECONDS);
		//token返回给前端
        Map<String, String> response = new HashMap<>();
        response.put("token", token);
        return response;
    }
}

编写全局拦截器

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class JwtTokenInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;
	
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 排除swagger访问接口
        String path = request.getRequestURI();
        if (path.contains("swagger") || path.contains("api-docs")) {
            return true;
        }

        String token = request.getHeader("Authorization");
		
        // 校验 Token
        if (token == null || !jwtTokenUtil.validateToken(token)) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("Please login first.");
            return false;
        }
		//todo 查询redis缓存的token进行比对,比对上放行请求
        return true;
    }
}

注册拦截器:

java 复制代码
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JwtTokenInterceptor()).addPathPatterns("/**");
    }
}

后记:

以上实现过程因项目不同会有所调整,这里只是提供一种能够实现的方案。

相关推荐
苏三说技术1 小时前
如何看待GPT-6在UP主众测中碾压夺冠?它是现在最强大模型吗?
后端
mldong2 小时前
一份 JSON,一条能跑的审批流:把报销流程送上工作流引擎
后端·架构
wno7042 小时前
Spring Boot WebFlux增删改查
java·spring boot·后端
Captaincc2 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
aramae3 小时前
模拟实现strlen()函数 (C语言)
c语言·开发语言·后端
行百里er4 小时前
Spring Insight 里如何把 Span 收成一条链路
spring boot·spring cloud·监控
IT_陈寒4 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
分支预测失败4 小时前
RISC-V 时间子系统深度专题:mtime 访问路径、SBI 定时器与 Linux tickless 协同
后端
65岁退休Coder4 小时前
PI Agent 开发一个生产级 Harness
后端·node.js·agent
xcl09254 小时前
24小时自助健身房系统软件开发实战指南:从架构到部署
java·spring boot