Loading

Redis应用_会话管理

Redis应用——会话管理

​ 会话管理的核心是跟踪用户的会话状态,通常为每个用户分配一个唯一的会话 ID(Session ID),将用户的相关信息存储在服务器端,并通过该 ID 进行关联和查询。Redis 可以作为存储会话信息的数据库,将会话 ID 作为键,用户信息作为值进行存储。

一、配置Redis环境

可以参考之前的文章:SpringBoot配置Redis环境

拦截器

这里我们使用Spring提供的拦截器,可在请求处理前后和完成后执行特定逻辑。

定义拦截器类

注入依赖 RedisTemplate,执行请求前的逻辑 :

@Component
public class RequestInterceptor implements HandlerInterceptor {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    	// 执行请求前的逻辑
        
        return true;
    }
}

配置拦截器

将拦截器类的对象作为参数添加进拦截器中:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private RequestInterceptor requestInterceptor;

    @Override
    public 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进行测试,随便写个接口,向这个接口发出请求:

image-20250318092134268

在发出请求时,是不携带任何参数的,也不携带cookie,因此在Cookie管理中是没有任何cookie的,可以在控制台看到打印的结果如下:

在发出请求后再打开Cookie管理,可以看到有了一条cookie,并且这条cookie会在下次发出请求时被携带:

控制台执行结果:

注意

问题

在代码中有一个点要非常注意,这个问题设计到 spring 的源码,需要对 Spring IOC 有一定了解。

这个问题就是在 WebConfig 中,当添加拦截器时,是需要将拦截器类的对象作为参数传进去的,因此除了上面添加拦截器的方法外,还有如下写法来添加拦截器:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public 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注入属性和依赖对象源码分析

posted @ 2025-03-18 10:43  maoxianjia  阅读(109)  评论(0)    收藏  举报