令牌主动失效机制范例(利用redis)注释分析

介绍该机制

  1. 令牌生成

    在需要限流的场景中,系统会根据一定的速率生成令牌,存储在 Redis 中。可以设定每秒生成的令牌数量。

  2. 令牌获取

    当用户请求时,系统会从 Redis 中获取令牌。可以使用原子性操作(如 DECR)来确保令牌的正确获取和减少。

  3. 令牌失效

    令牌的主动失效可以通过设定过期时间(TTL)来实现。当生成的令牌在一定时间内未被消费,Redis 会自动删除这些令牌。也可以通过在逻辑上判断令牌的使用情况,主动将过期的令牌从队列中剔除。

  4. 监控与调整

    在实际运行中,可以通过 Redis 的发布/订阅机制或键空间通知(Keyspace Notifications)来监控令牌的状态,动态调整生成速率或失效策略。

redis令牌失效机制的代码实现案例

redis配置

pom、yml(redis配置)

redis的配置文件

package com.itheima.config;

import org.springframework.cache.annotation.CachingConfigurerSupport; // 引入缓存支持类
import org.springframework.context.annotation.Bean; // 引入Bean注解
import org.springframework.context.annotation.Configuration; // 引入配置注解
import org.springframework.data.redis.connection.RedisConnectionFactory; // 引入Redis连接工厂
import org.springframework.data.redis.core.RedisTemplate; // 引入通用Redis模板
import org.springframework.data.redis.core.StringRedisTemplate; // 引入字符串Redis模板

@Configuration // 声明这是一个配置类
public class RedisConfig extends CachingConfigurerSupport { // 继承缓存支持类

    // 定义一个StringRedisTemplate Bean,用于操作String类型的键值对
    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 使用提供的连接工厂创建StringRedisTemplate实例
        return new StringRedisTemplate(redisConnectionFactory);
    }

    // 定义一个通用的RedisTemplate Bean,用于操作任意类型的对象
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 创建RedisTemplate实例
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 设置连接工厂,以便连接到Redis服务器
        template.setConnectionFactory(redisConnectionFactory);
        // 返回配置好的RedisTemplate实例
        return template;
    }
}

用户控制器(登录)

usercontroller

    @PostMapping("/login")
    public Result<String> login(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password) {
        //根据用户名查询用户
        User loginUser = userService.findByUserName(username);
        //判断该用户是否存在
        if (loginUser == null) {
            return Result.error("用户名错误");
        }

        //判断密码是否正确  loginUser对象中的password是密文
        if (Md5Util.getMD5String(password).equals(loginUser.getPassword())) {
            //登录成功
            Map<String, Object> claims = new HashMap<>();  // 创建一个存储用户信息的映射
            claims.put("id", loginUser.getId());           // 将用户 ID 存储在 claims 中
            claims.put("username", loginUser.getUsername()); // 将用户名存储在 claims 中
            String token = JwtUtil.genToken(claims);       // 生成 JWT,包含用户的 ID 和用户名
            //把token存储到redis中
            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            operations.set(token,token,1, TimeUnit.HOURS);
            return Result.success(token);
        }
        return Result.error("密码错误");
    }

使用完需要删除操作(usercontroller)

        //删除redis中对应的token
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        operations.getOperations().delete(token);
        return Result.success();

拦截器里redis的验证

logininterceptor
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Autowired
    private StringRedisTemplate stringRedisTemplate; // 注入 Redis 模板,用于操作 Redis 数据库
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 令牌验证
        String token = request.getHeader("Authorization"); // 从请求头中获取名为 "Authorization" 的令牌
        // 验证 token
        try {
            // 从 Redis 中获取与请求中相同的 token
            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            String redisToken = operations.get(token); // 查询 Redis 数据库
            if (redisToken == null) {
                // 如果 Redis 中没有该 token,说明 token 已经失效
                throw new RuntimeException("Token has expired"); // 抛出异常以指示 token 失效
            }
            // 解析 token,获取业务数据(例如用户信息)
            Map<String, Object> claims = JwtUtil.parseToken(token);
            // 将业务数据存储到 ThreadLocal 中,以便后续处理使用
            ThreadLocalUtil.set(claims);
            // 放行请求,继续处理
            return true;
        } catch (Exception e) {
            // 如果发生异常(例如 token 失效),设置 HTTP 响应状态码为 401(未授权)
            response.setStatus(401);
            // 拦截请求,不放行
            return false;
        }
    }
}

测试

redisTest

package com.itheima;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import java.util.concurrent.TimeUnit;

@SpringBootTest     //如果在测试类上添加了这个注解,那么将来单元测试方法执行之前,会先初始化Spring容器
public class RedisTest {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    public void testSet(){
        //往redis中存储一个键值对  StringRedisTemplate
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();

        operations.set("username","zhangsan");
        operations.set("id","1",15, TimeUnit.SECONDS);
    }

    @Test
    public void testGet(){
        //从redis中获取一个键值对
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        System.out.println(operations.get("id"));

    }
}
相关推荐
Dlwyz1 小时前
redis-击穿、穿透、雪崩
数据库·redis·缓存
Theodore_10222 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
工业甲酰苯胺3 小时前
Redis性能优化的18招
数据库·redis·性能优化
冰帝海岸3 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象4 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了4 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
小二·5 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic5 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
懒洋洋大魔王5 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康5 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud