SpringBoot+JWT+redis实现拦截器(每次请求更新token过期时间)

该博客介绍了使用Java和Spring Boot结合Redis实现Token登录验证的方法。Redis用于存储Token及登录有效期,每次请求延长过期时间,若规定时间内无请求则登录失效。还给出了创建相关Java类、配置文件的步骤,以及Controller登录处理和请求例子。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

条件

  1. redis(用来存储token以及登录有效期,如果没有不用redis,token可以存在session或者cookie里)
  2. 每次请求延长过期时间.生成的token有效期设置长一些,存到redis里面,每次请求查看redis是否有token,如果有请求成功redis延长,如果没有就代表在规定时间内没有请求过则登录失效,重新登录,以此循环.

废话不多说,上代码(看注释)

  1. pom.xml
		<dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.19.1</version>
        </dependency>
  1. 创建JwtUtil.java 用token获取用户id,以及使用用户id生成token(可以加上其他信息一起)
package com.ly.remind.common.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.ly.remind.common.constants.CommonConstants;
import lombok.Data;

import java.util.Calendar;

/**
 * @ClassName: JWTUtil.java
 * @Program: com.ly.remind.common.util.JwtUtil
 * @Date: 2022/10/26 上午10:27
 * @Author: Zhaop
 * @Description: jwt token util
 */
@Data
public class JwtUtil {
    // 任意字符串
    private static final String SING = "!A;E]R'T'!S-*G-S*'S[;HS.HH]D*S-VS+D=GS-=";

    private static String token;
    
    // 过期时间 小时 可以设的长一点,通过redis的过期时间来决定用户登录的有效时长
    private static final Integer EXPIRATION_TIME = 24*30;


    /***
     *@Author: Zhaop
     *@Params: [id, ttlMinute:过期时间,分钟]
     *@Return: java.lang.String
     *@Date: 2022/10/26 下午12:31
     *@Description: 生成用户token
     *@Notes:
     */
    public static String getJWToken(String id) {

        Calendar instance = Calendar.getInstance();
        // 设置过期时间
        instance.add(Calendar.HOUR, EXPIRATION_TIME);

        JWTCreator.Builder builder = JWT.create();
        // 指定标识字段
        builder.withClaim(CommonConstants.JwtConstants.TOKEN_KEY, id);
        // 指定过期时间
        token = builder.withExpiresAt(instance.getTime())
                // 指定生成算法及签名
                .sign(Algorithm.HMAC256(SING));

        return token;
    }

    /***
     *@Author: Zhaop
     *@Params: [token]
     *@Return: boolean
     *@Date: 2022/10/26 下午12:36
     *@Description: 验证token, 返回true或false
     *@Notes:
     */
    public static boolean verify(String token) {
        try {
            JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /****
     *@Author: Zhaop
     *@Params: [token]
     *@Return: com.auth0.jwt.interfaces.DecodedJWT
     *@Date: 2022/10/26 下午12:35
     *@Description: 验证token, 正确通过, 否则抛出异常
     *@Notes:
     */
    public static DecodedJWT verifyToken(String token) {
        return JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
    }

    /***
     *@Author: Zhaop
     *@Params: [token]
     *@Return: int
     *@Date: 2022/10/26 下午12:35
     *@Description: 从token中获取用户id
     *@Notes:
     */
    public static String getIdByToken(String token) {
        DecodedJWT verify = JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
        return verify.getClaim(CommonConstants.JwtConstants.TOKEN_KEY).asString();
    }


}



  1. 创建jwt.yaml 也可以直接写在方法里或者application.properties/yaml里,写在这方便维护看自己需求
jwt:
  # 需要拦截的路径,逗号分割 不能带引号
  verify: /**
  # 需要放行的路径,逗号分割 不能带引号
  skip: /api/**
  1. 创建WebMvcConfig.java 实现WebMvcConfigurer接口做拦截逻辑(重要)
package com.ly.remind.config;

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.ly.remind.common.constants.CommonConstants;
import com.ly.remind.common.constants.RedisConstants;
import com.ly.remind.common.enums.ResponseEnum;
import com.ly.remind.common.util.JwtUtil;
import com.ly.remind.common.util.RedisUtil;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @ClassName: WebMvcConfig.java
 * @Program: com.ly.remind.config.WebMvcConfig
 * @Date: 2022/10/26 上午10:33
 * @Author: Zhaop
 * @Description: 拦截器以及拦截处理
 * 
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "jwt") 
@PropertySource(value = {"classpath:jwt.yaml"})
public class WebMvcConfig implements WebMvcConfigurer {
    @Value("${verify}")
    private String[] verify;
    @Value("${skip}")
    private String[] skip;

    @Autowired
    private RedisUtil redisUtil;
    /***
     *@Author: Zhaop
     *@Params: [request, response, handler]
     *@Return: boolean
     *@Date: 2022/10/26 下午6:35
     *@Description: 拦截处理
     *@Notes: 
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 用户拦截器
        registry.addInterceptor(
                new HandlerInterceptor() {
                    @Override
                    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                        // 如果不是映射到方法直接通过
                        if (!(handler instanceof HandlerMethod)) {
                            return true;
                        }
						
						// 从请求中获取token
                        String token = request.getHeader("token");

                        // 捕获刚刚JWT中抛出的异常,并封装对应的返回信息
                        try {
                             // 通过token获取id
                            String userId = JwtUtil.getIdByToken(token);
                            String s = redisUtil.get(RedisConstants.USER_TOKEN_KEY + userId);
                            // token不存在,或者token不同均不放行
                            if (StringUtils.isBlank(s) || !token.equals(s)) {
                                throw new TokenExpiredException("未登录!");
                            }
                            //请求成功 redis 延长登录的过期时间
                            redisUtil.setExOfMill(RedisConstants.USER_TOKEN_KEY + userId, token, CommonConstants.JwtConstants.EXPIRATION_TIME_30M);
                            // 验证通过刷新redis-token
                            return true;
                        } catch (SignatureVerificationException e) {
                            // 无效签名
                            ResponseEnum.USER_NOT_LOGGED_IN.assertNotNull(null);
                        } catch (TokenExpiredException e) {
                            // 已过期
                            ResponseEnum.USER_NOT_LOGGED_IN.assertNotNull(null);
                        } catch (AlgorithmMismatchException e) {
                            // 算法不一致
                            ResponseEnum.USER_NOT_LOGGED_IN.assertNotNull(null);
                        } catch (Exception e) {
                            // 无效身份信息
                            ResponseEnum.USER_NOT_LOGGED_IN.assertNotNull(null);
                        }
                        return false;
                    }
                }
        )
                // 需要拦截的请求
                .addPathPatterns(verify)
                // 需要放行的请求
                .excludePathPatterns(skip);
    }

    /**
     * 跨域支持
     *
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }

    @Bean
    public WebMvcConfig getWebMvcConfig() {
        return new WebMvcConfig();
    }

}

  1. Controller登录中的处理 主要是登录之后的处理
package com.ly.remind.controller;


import com.ly.remind.common.constants.CommonConstants;
import com.ly.remind.common.constants.RedisConstants;
import com.ly.remind.common.resp.ResultMessage;
import com.ly.remind.common.util.JwtUtil;
import com.ly.remind.common.util.RedisUtil;
import com.ly.remind.service.IUserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * <p>
 * 用户信息表 前端控制器
 * </p>
 *
 * @author Zhaop
 * @since 2022-10-25
 */
@RestController
public class UserController {
    @Resource
    private IUserService iUserService;
    @Resource
    private RedisUtil redisUtil;

    @PostMapping("/api/loggin")
    public ResultMessage login() {
    	// user-id123就是用户id,我这里直接写死了,程序中改成动态的
        String token = JwtUtil.getJWToken("user-id123");
        // 存储到redis中,在禁用用户的同时可以强制踢下线 过期时间30分钟
        redisUtil.setExOfMill(RedisConstants.USER_TOKEN_KEY + "user-id123", token, CommonConstants.JwtConstants.EXPIRATION_TIME_30M);
        ResultMessage resultMessage = new ResultMessage();
        resultMessage.setCode(1);
        resultMessage.setMsg("ok");
        // token返回给前端,每次请求需带上(放在请求头)
        resultMessage.setData(token);
        return resultMessage;
    }

    @GetMapping("/user/userinfo")
    public ResultMessage userinfo() {
        ResultMessage resultMessage = new ResultMessage();
        resultMessage.setCode(1);
        resultMessage.setMsg("ok");
        return resultMessage;
    }
}
  1. 请求例子
    在这里插入图片描述

点赞加关注,代码不迷路~
点赞加关注,代码不迷路~
点赞加关注,代码不迷路~

### 使用 Spring BootRedisJWT 实现身份验证 在现代 Web 应用程序开发中,使用 JWT(JSON Web Token)作为无状态的身份验证机制是一种常见的方式。为了增强系统的安全性并支持分布式部署场景下的会话管理,可以结合 Redis 来存储和管理 JWT 的相关信息。 以下是基于 Spring Boot 3 + Spring Security 6 + JWT + Redis实现方案: #### 1. **项目依赖** 首先,在 `pom.xml` 文件中添加必要的依赖项: ```xml <dependencies> <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- JWT --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <scope>runtime</scope> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <scope>runtime</scope> <version>0.11.5</version> </dependency> <!-- Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- MySQL and MyBatis (Optional, if using database for user management) --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.1</version> </dependency> </dependencies> ``` --- #### 2. **JWT 工具类** 创建一个工具类用于生成和解析 JWT Token。 ```java import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; public class JwtUtil { private static final String SECRET_KEY = "your_secret_key"; // 密钥需安全保存 public String generateToken(String username) { return Jwts.builder() .setSubject(username) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } public Claims parseToken(String token) { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); } } ``` --- #### 3. **Redis 配置** 通过 Redis 缓存用户的 Token 及其有效期。 ```java @Configuration public class RedisConfig { @Bean public LettuceConnectionFactory redisConnectionFactory() { return new LettuceConnectionFactory(); // 默认连接本地 Redis } @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); // JSON序列化 return template; } } ``` --- #### 4. **登录逻辑** 当用户成功登录后,生成 JWT 并将其存储到 Redis 中。 ```java @RestController @RequestMapping("/auth") public class AuthController { @Autowired private UserService userService; // 假设有一个服务来处理用户数据 @Autowired private JwtUtil jwtUtil; @Autowired private RedisTemplate<String, Object> redisTemplate; @PostMapping("/login") public ResponseEntity<?> login(@RequestBody LoginRequest request) { User user = userService.authenticate(request.getUsername(), request.getPassword()); if (user != null) { String token = jwtUtil.generateToken(user.getUsername()); // 将 Token 存入 Redis设置过期时间为 1 天 redisTemplate.opsForValue().set("AUTH:" + token, user.getId(), Duration.ofDays(1)); return ResponseEntity.ok(Map.of("token", token)); } else { throw new RuntimeException("Invalid credentials"); } } } ``` --- #### 5. **拦截器与过滤器** 在请求到达控制器之前,检查 Token 是否有效以及是否存在 Redis 中。 ```java @Component public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private JwtUtil jwtUtil; @Autowired private RedisTemplate<String, Object> redisTemplate; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { try { String token = authHeader.substring(7); // 提取 Token // 检查 Token 是否存在于 Redis 中 Boolean existsInRedis = redisTemplate.hasKey("AUTH:" + token); if (!existsInRedis) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } // 解析 Token 获取用户名 Claims claims = jwtUtil.parseToken(token); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( claims.getSubject(), null, Collections.emptyList() ); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (Exception e) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); return; } } filterChain.doFilter(request, response); } } ``` --- #### 6. **Spring Security 配置** 配置 Spring Security 以启用自定义过滤器。 ```java @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeHttpRequests() .requestMatchers("/auth/login").permitAll() .anyRequest().authenticated() .and() .addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } } ``` --- ### 总结 上述方法展示了如何利用 Spring BootRedisJWT 构建一套完整的身份验证解决方案[^1]。该方案不仅实现了无状态的 Token 认证,还借助 Redis 提供了高效的 Token 管理能力[^4]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhopq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值