一、背景

在微服务或集群部署场景下,传统的内存式 HTTP Session 无法在多实例间共享,会导致同一用户在不同实例上重复登录。

那么,就可以借助 Redis 作为统一的 Session 存储,实现分布式 Session,并结合自定义拦截器(HandlerInterceptor)在每次请求时校验用户登录状态。

二、为什么用Redis

单实例模式:Session 存储在单机内存,性能快,但无法横向扩展。

集群模式:若不开启 Session 复制,每台机器维护自己的内存 Session,用户在切换实例后 Session 丢失;若使用 Tomcat Session 复制,网络开销大、维护成本高。

三、方案概述

1、Redis 统一存储:所有实例将 Session 数据读写到 Redis,保证各实例共享同一份 Session。

2、Spring Session 集成:使用 Spring Session + Spring Data Redis,无侵入地替换容器原生 Session。

3、自定义拦截器:实现 HandlerInterceptor,在每次请求到达 Controller 之前,从 Session 中获取用户信息,若未登录则重定向到登录页。

HTTP Session图 (转载出处:HTTP Session图)

  • 客户端第一次访问,服务端创建一个 HttpSession,生成唯一 ID(JSESSIONID),并通过 Cookie 返回给客户端。

  • 客户端后续请求带上该 Cookie,服务端根据 ID 查找并返回对应 Session 对象。

  • 我们发现:默认 Session 存储在容器内存,跨实例无法共享

 解决方案:Redis 存储 Session 原理

  • Spring Session 将原生 HttpSession 接口的实现替换为 RedisOperationsSessionRepository

  • 每次 session.setAttribute(...),会把属性序列化写入 Redis

  • 请求进来时,通过 sessionId 从 Redis 读取并反序列化,还原成 HttpSession

  • 集群中所有实例都连接同一 Redis,实现共享

四、实施步骤

1、环境准备

JDK 1.8以上、Spring Boot 2以上、Spring Session Data Redis、Redis 

2、导入依赖

<dependencies>
  <!-- Spring Boot Web -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <!-- Spring Session + Redis -->
  <dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
  </dependency>
  <!-- Spring Data Redis -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
  </dependency>
</dependencies>

3、Rdis连接

spring:
  redis:
    host: localhost
    port: 6379
  session:
    store-type: redis     # 启用 Redis 存储 Session
    timeout: 1800s        # Session 过期时间

4、Redis配置

Spring Boot 自动装配了 RedisConnectionFactory 和 RedisTemplate,可自定义序列化策略:

@Configuration
public class RedisConfig {

    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        // 使用 JSON 序列化,避免 JDK 序列化导致的可读性差
        return new GenericJackson2JsonRedisSerializer();
    }

    @Bean
    public RedisSerializer<Object> defaultRedisSerializer() {
        return new StringRedisSerializer();
    }
}

 5、启用 Spring Session

指定 Session 前缀和超时时间:

@EnableRedisHttpSession(
    maxInactiveIntervalInSeconds = 1800,    // 30 分钟
    redisNamespace = "myapp:sessions"       // Redis Key 前缀
)
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

6、自定义登录拦截器

实现 HandlerInterceptor,在请求前校验用户是否已登录

@Component
public class LoginInterceptor implements HandlerInterceptor {

    // 不拦截的白名单    private static final List<String> WHITELIST = Arrays.asList(
        "/login", "/register", "/css/", "/js/"
    );

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        String path = request.getRequestURI();
        // 放行白名单
        for (String prefix : WHITELIST) {
            if (path.startsWith(prefix)) {
                return true;
            }
        }

        // 从 Session 中获取用户信息
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute("user") != null) {
            // 已登录,继续请求
            return true;
        }

        // 未登录,返回 401 或重定向
        if (isAjax(request)) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("Unauthorized");
        } else {
            response.sendRedirect("/login");
        }
        return false;
    }

    private boolean isAjax(HttpServletRequest request) {
        String header = request.getHeader("X-Requested-With");
        return "XMLHttpRequest".equals(header);
    }
}

7、拦截器注册

将拦截器加入 Spring MVC 拦截链

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**");   // 拦截所有请求
    }
}

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐