带你实现基于 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 管理。

相关推荐
JavaGuide4 天前
字节二面:Redis 能做消息队列吗?怎么实现?
redis·后端
漫霂4 天前
基于redis实现登录校验
redis·后端
程序员小崔日记4 天前
一篇文章彻底搞懂 MySQL 和 Redis:原理、区别、项目用法全解析(建议收藏)
redis·mysql·项目实战
读书笔记4 天前
CentOS 7 安装 redis-6.2.6.tar.gz 详细步骤(从源码编译到启动配置)
redis
焗猪扒饭5 天前
redis stream用作消息队列极速入门
redis·后端·go
雨中飘荡的记忆7 天前
大流量下库存扣减的数据库瓶颈:Redis分片缓存解决方案
java·redis·后端
曲幽7 天前
FastAPI分布式系统实战:拆解分布式系统中常见问题及解决方案
redis·python·fastapi·web·httpx·lock·asyncio
知我Deja_Vu12 天前
redisCommonHelper.generateCode(“GROUP“),Redis 生成码方法
数据库·redis·缓存