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

相关推荐
我是一颗柠檬1 天前
【Java后端技术亮点】热Key探测与本地缓存二级防护:Redis热点问题的终极解决方案
java·redis·后端·缓存·中间件
cfm_29141 天前
Redis高并发缓存架构设计与性能优化实战
redis·缓存·性能优化
画江湖Test1 天前
Redis 块的原理
数据库·redis·缓存·性能优化
海市公约1 天前
Redis主从复制全量同步七步时序与命令传播机制详解
数据库·redis·缓存·主从复制·高可用架构·全量同步
小马爱打代码1 天前
Redis 和 MySQL 双写一致性:延迟双删、读写锁、MQ、Canal 怎么选?
数据库·redis·mysql
我,也来自江湖1 天前
Redis的持久化有哪些方式
数据库·redis·缓存
小小工匠1 天前
Redis - 实现分页 + 多条件模糊查询:一套完整可落地的组合方案
数据库·redis·缓存·分页·模糊查询
阿演1 天前
DataDjinn v0.1.6 更新:增加在线更新功能,Redis 数据源支持,表格预览和连接体验继续增强
数据库·redis·缓存·数据库连接工具
Trouvaille ~2 天前
【Redis篇】Redis 渐进式遍历与数据库管理
数据库·redis·缓存·中间件·数据库管理·后端开发·scan
Byron__2 天前
Redis高频面试:数据结构+编码+分布式锁+缓存问题
redis·缓存·面试