Redis应用——会话管理
会话管理的核心是跟踪用户的会话状态,通常为每个用户分配一个唯一的会话 ID(Session ID),将用户的相关信息存储在服务器端,并通过该 ID 进行关联和查询。Redis 可以作为存储会话信息的数据库,将会话 ID 作为键,用户信息作为值进行存储。
一、配置Redis环境
可以参考之前的文章:SpringBoot配置Redis环境
拦截器
这里我们使用Spring提供的拦截器,可在请求处理前后和完成后执行特定逻辑。
定义拦截器类
注入依赖 RedisTemplate,执行请求前的逻辑 :
@Component
public class RequestInterceptor implements HandlerInterceptor {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 执行请求前的逻辑return true;}
}
配置拦截器
将拦截器类的对象作为参数添加进拦截器中:
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate RequestInterceptor requestInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(requestInterceptor).addPathPatterns("/**");}
}
cookie处理逻辑
在拦截器RequestInterceptor中定义preHandle的执行逻辑
- 从请求中获取cookie
- 如果存在cookie的话,根据cookie去redis中获取用户数据
- 如果没有获取到用户数据的话,要么就是请求中不含cookie,要么就是redis中没有存用户数据,因此需要重新生成一个cookie,并将cookie作为键,用户数据作为值存储在redis中
- 将cookie放到响应数据中返回
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {Cookie[] lckcookies = request.getCookies();String cookie = null;if (lckcookies != null){for (Cookie lckcookie : lckcookies){if (lckcookie.getName().equals("lckcookie")){cookie = lckcookie.getValue();}}}String userId = null;if (cookie != null){userId = (String) redisTemplate.opsForValue().get(cookie);}if (userId == null){// 生成一个唯一的 Cookie 值,这里使用 UUID 作为示例String newCookieValue = UUID.randomUUID().toString();redisTemplate.opsForValue().set(newCookieValue,"user123",3600, TimeUnit.SECONDS);// 创建一个新的 Cookie 对象Cookie newCookie = new Cookie("lckcookie", newCookieValue);// 设置 Cookie 的属性,例如有效期、路径等// 设置 Cookie 的有效期为 3600 秒(1 小时)newCookie.setMaxAge(3600);// 设置 Cookie 的路径为根路径newCookie.setPath("/");// 将新的 Cookie 添加到响应中response.addCookie(newCookie);}System.out.println(cookie);System.out.println("拦截器拦截请求");return true;
}
测试
这里我用到Apifox进行测试,随便写个接口,向这个接口发出请求:


在发出请求时,是不携带任何参数的,也不携带cookie,因此在Cookie管理中是没有任何cookie的,可以在控制台看到打印的结果如下:
在发出请求后再打开Cookie管理,可以看到有了一条cookie,并且这条cookie会在下次发出请求时被携带:

控制台执行结果:

注意
问题
在代码中有一个点要非常注意,这个问题设计到 spring 的源码,需要对 Spring IOC
有一定了解。
这个问题就是在 WebConfig 中,当添加拦截器时,是需要将拦截器类的对象作为参数传进去的,因此除了上面添加拦截器的方法外,还有如下写法来添加拦截器:
@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/**");}
}
但是这个添加拦截器的方法在实际执行拦截逻辑时会报错,并且报的是 redisTemplate 为空的错:
这是为啥呢?
依赖注入问题
在 RequestInterceptor
类中,使用了 @Autowired
注解来注入 RedisTemplate<String, Object>
对象,代码如下:
@Autowired
private RedisTemplate<String, Object> redisTemplate;
当使用 new RequestInterceptor()
手动创建 RequestInterceptor
对象时,Spring 框架不会对这个手动创建的对象进行依赖注入。也就是说,redisTemplate
字段将为 null
。在后续调用 redisTemplate
的方法时,会抛出 NullPointerException
异常。
Bean 生命周期管理问题
Spring 框架负责管理 Bean 的生命周期,包括创建、初始化、销毁等过程。当你使用 @Component
注解将 RequestInterceptor
类标记为 Spring 组件时,Spring 会自动将其创建为一个 Bean,并对其进行管理。
而使用 new RequestInterceptor()
手动创建的对象,不受 Spring 框架的管理,它不会经历 Spring 提供的初始化和销毁等生命周期回调方法。
具体的可以看这篇文章:Spring注入属性和依赖对象源码分析