带你实现基于 Redis 的分布式 Session 管理

咱们就把自定义注解想象成现实生活中的"安检盖章"。

在你的"在线英语学习平台"中,有很多接口(比如查看成绩、修改头像)。如果没有注解,你每个接口都要手动写一遍检查 Token 的代码,就像每个登机口都要放一台巨大的安检机,太累了。 为了避免在每个 Controller 接口里重复写 Token 校验代码,我们采用了 "注解标记 + 切面拦截" 的方案。

流程

  1. 创建文件 :在 IDEA 里 New -> Java Class,选 Annotation,命名为 CheckToken
  2. 写定义代码:CheckToken
  3. 配置拦截器 :这是最难的一步,需要写一个类实现 HandlerInterceptor 接口。

1. 通俗理解:它就是一个"便利贴"

自定义注解本质上就是一张"有特殊含义的便利贴"。

  • 第一步(定义) :你设计了一张红色的便利贴,上面印着"必须安检"四个字。
  • 第二步(标记) :你把这张便利贴啪地一下,贴在某个接口(方法)的大门上。
  • 第三步(执行) :你请了一个保安(拦截器/切面),告诉他:"凡是门上贴了红色便利贴的,必须查他有没有 Token,没带的不准进!"

2. 细致拆解:代码里的三个关键零件

要完成今天的任务,你需要写三个部分的代码。咱们一个一个看:

零件 A:定义那张"便利贴" (Annotation)

这只是个声明,告诉 Java 这是一个注解。

project.template.annotation

less 复制代码
@Target(ElementType.METHOD)//贴在方法而不是类上
@Retention(RetentionPolicy.RUNTIME)//运行时生效同时让拦截器看见
public @interface CheckToken {
    boolean required() default true;
}

零件 B:那个"保安" (Interceptor / AOP)

这是最细致的地方。注解本身没法干活,必须有一个程序去读取它。这就是拦截器的作用。

保安的工作流程如下:

  1. 看门牌 :请求过来时,先看这个方法上有没有贴 @CheckToken
  2. 搜身 :如果有贴,就从请求头(Header)里翻找 token
  3. 对名单 :拿着 tokenRedis 里查:
    • 查到了 :说明 Token 没过期。保安会做一件事------ "续命" 。调用 redisTemplate.expire(key, 30, MINUTE),把你的有效期重新拨回 30 分钟。
    • 查不到 :说明你是"黑户"或者过期了,保安直接把你拦住,返回错误信息(比如:401 未登录)。
less 复制代码
@Aspect //告诉程序这是切面
@Component//注入Spring
@Slf4j//引入日志框架
public class TokenCheckAop {
    @Autowired
    private RedisService redisService;//注入Redis

    @Autowired
    private StringRedisTemplate redisTemplate;//用于续期
    //定义切点,有@CheckToken注释的方法都要执行
    @Pointcut("@annotation(com.kt.project.template.annotation.CheckToken)")
    public void checkTokenPointcut() {
    }

    //在执行方法前拦截
    @Before("checkTokenPointcut()")
    public void doBefore() {
        HttpServletRequest request = ((ServletRequestAttributes)Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        //从请求头Header中尝试获取token
        String token = request.getHeader("token");
        log.info("token校验中,获取到的Token为:{}",token);
        //校验:没带token
        if (token == null || token.isEmpty()) {
            throw new RuntimeException("请在Header中携带token");
        }
        //拿token去redis查 (Redis里面的getString类型)
        String userId = redisService.getString(token);
        if (userId == null) {//Redis里面查不到,说明过期了或者是假token
            throw new RuntimeException("Token已过期或不存在,请重新登录");
        }
        //查到了token,就延期时间
        redisTemplate.expire(token,30, TimeUnit.MINUTES);
        log.info("Token校验通过,用户{},已自动续期30分钟",userId);
    }
}

编写 TokenCheckAop 切面类,充当系统的"安检保安"。它会扫描所有贴了 @CheckToken 的方法,并执行"拦截-校验-续期"的一站式逻辑。

  • 拦截逻辑 :通过 Pointcut 精准定位标注了 @CheckToken 的方法。
  • 校验与续期:从请求头获取 Token,去 Redis 查询;若存在则自动延长有效期,实现"滑动过期"。

零件 C:贴贴纸 (Controller)

这是你最舒服的一步,只需要在方法上面写一行字:@CheckToken

less 复制代码
@CheckToken  // 自动触发 AOP 保安逻辑
@GetMapping("/get")
public String get(String key) {
    // 只有经过 AOP 校验通过后,这里的业务代码才会执行
    return "校验通过,查询结果为:" + redisService.getString(key);
}

3. 为什么要这么折腾?

如果你直接在 Controller 里写校验,代码会变成这样:

  • 接口 A:验证 Token -> 续期 -> 查数据库 -> 返回数据。
  • 接口 B:验证 Token -> 续期 -> 查数据库 -> 返回数据。

用注解后的好处(专业话术):

  • 解耦:校验逻辑(保安)和业务逻辑(做题/查分)分开了,互相不干扰。
  • 复用:以后再写 100 个接口,我只要在上面打个"贴纸"就行了,一秒钟搞定。

你成功实现了基于 Redis 的分布式 Session 管理。

相关推荐
摇滚侠13 小时前
Redis 秒杀功能 超卖问题 一人一单问题 分布式锁 精彩!精彩!
redis·分布式·bootstrap
Emily呀18 小时前
【无标题】
redis
愈努力俞幸运19 小时前
function calling与mcp
android·数据库·redis
IronMurphy19 小时前
Redis拷打第一讲
数据库·redis·缓存
楠枬20 小时前
Redis 事务
数据库·redis·缓存
摇滚侠1 天前
Redis 查询接口加缓存 缓存雪崩 缓存穿透 缓存击穿 精彩!精彩!
redis·缓存
Mr. zhihao1 天前
[特殊字符] 从 Redis 缓存穿透到布隆过滤器,再到布谷鸟过滤器:一次穿透防护的进化之旅
数据库·redis·缓存
@小匠1 天前
Redis 7 持久化机制
数据库·redis·缓存
phltxy1 天前
Redis 核心数据类型之 String 详解
数据库·redis·bootstrap
码哥字节1 天前
开多个 Agent 后 Claude Code 账单翻了 4 倍,一个配置解决了
redis·性能