Spring MVC中HttpSession的详解

发布于:2025-08-08 ⋅ 阅读:(112) ⋅ 点赞:(0)

1.HttpSession基础概念与作用

1.1 会话管理的核心需求

在Web应用中,HTTP协议本身是无状态的,这意味着服务器无法直接识别用户身份或保持用户状态。每次请求都独立于之前的请求,这给需要用户状态的应用(如登录验证、购物车)带来了挑战。为了解决这一问题,会话管理(Session Management)成为Web开发中的关键技术。

会话管理的核心需求包括

  1. 用户身份识别:能够将用户与特定的会话关联起来,即使用户在多个页面间跳转。
  2. 状态保持:在不同请求之间保持用户状态,如购物车内容、用户偏好设置等。
  3. 并发控制:管理同一用户在不同设备上的多个会话,防止会话劫持。
  4. 安全性:防止会话固定攻击、XSS攻击等安全威胁。

这些需求使得HttpSession成为Web应用中不可或缺的组件,它为开发者提供了一种在服务器端管理用户会话状态的机制。

1.2HttpSession与Cookie的协同机制

HttpSession的实现依赖于Cookie技术,两者协同工作以保持用户会话状态。HttpSession通过JSESSIONID这一特殊的Cookie标识用户会话,具体机制如下:

1.2.1Session ID的生成与传递

当用户第一次访问服务器时,如果请求需要会话,服务器会执行以下步骤:

  1. 生成唯一Session ID:服务器创建一个全局唯一的Session ID,通常使用加密算法生成,以防止预测。
  2. 创建HttpSession对象:服务器在内存中创建一个HttpSession对象,并与生成的Session ID关联。
  3. 设置Cookie:服务器将Session ID作为名为JSESSIONID的Cookie发送给客户端,该Cookie默认具有HttpOnly属性,防止通过JavaScript访问。

客户端在后续请求中,会自动将JSESSIONID Cookie发送回服务器。服务器根据这个Cookie中的Session ID查找对应的HttpSession对象,从而保持会话状态。

1.2.2Cookie的安全属性

为增强安全性,JSESSIONID Cookie可配置以下安全属性:

属性 作用 配置方式
HttpOnly 防止通过JavaScript访问Cookie server.servlet.session.cookie.http-only=true
Secure 仅通过HTTPS传输Cookie server.servlet.session cookie.secure=true
SameSite 限制Cookie随跨站请求发送 server.servlet.session.cookie same-site=Lax

这些属性可有效防御XSS攻击和CSRF攻击,是现代Web应用中Session管理的重要安全措施。

1.2.3Session ID的存储方式

除了Cookie外,Session ID还可通过以下方式传递:

  1. URL重写:在URL中附加;jsessionid=...参数,适用于不支持Cookie的客户端。
  2. POST参数:在表单提交中包含jsessionid参数。
  3. HTTP头:通过自定义HTTP头传递Session ID,适用于RESTful API。

在Spring MVC中,默认使用Cookie传递Session ID,但可通过配置支持URL重写等其他方式。

1.3HttpSession的典型应用场景

HttpSession在Web应用中有着广泛的应用场景,主要包括:

1.3.1用户登录状态管理

最常见的应用场景是管理用户登录状态。当用户登录成功后,可在Session中存储用户信息,如用户ID、角色等。示例代码:

// 用户登录成功后存储用户信息
 session.setAttribute("LOGGED_USER", user);
// 检查用户是否已登录
 Object user = session.getAttribute("LOGGED_USER");
1.3.2购物车实现

电商应用中,购物车信息通常存储在Session中,确保用户在不同页面间浏览时,购物车内容保持一致。示例代码:

// 添加商品到购物车
 List<Product> cart = (List<Product>) session.getAttribute("CART");
 if (cart == null) {
     cart = new ArrayList<>();
     session.setAttribute("CART", cart);
 }
 cart.add(product);

// 获取购物车内容
 List<Product> cart = (List<Product>) session.getAttribute("CART");
1.3.3表单数据暂存

在多步骤表单提交过程中,可使用Session暂存中间步骤的数据。示例代码:

// 第一步提交
 session.setAttribute("STEP1_DATA", data);
// 第二步提交
 session.setAttribute("STEP2_DATA", data);
// 最终处理
 Object step1Data = session.getAttribute("STEP1_DATA");
 Object step2Data = session.getAttribute("STEP2_DATA");
1.3.4并发控制

在需要限制用户同时登录次数的场景中,可通过Session管理实现并发控制。Spring Security提供了并发会话控制功能,具体配置将在后续章节详细说明。

1.3.5Flash属性传递

在重定向场景中,可通过Flash属性在一次请求间传递数据,而不会将数据暴露在URL中。这是Spring MVC特有的功能,将在第2.3节详细讲解。

2. Spring MVC中的HttpSession管理

2.1原生HttpSession操作方式

在Spring MVC中,可通过多种方式操作HttpSession,包括直接使用Servlet API和Spring提供的便捷方法。

2.1.1通过Controller方法参数注入

Spring MVC支持直接在Controller方法参数中注入HttpSession对象,无需显式从HttpServletRequest中获取。示例代码:

@Controller
public class UserController {

    @GetMapping("/login")
    public String loginForm() {
        return "login";
    }

    @PostMapping("/login")
    public String login(@RequestParam("username") String username,
                           @RequestParam("password") String password,
                           HttpSession session) {
        // 验证用户
        User user = userService.validate(username, password);
        if (user != null) {
            session.setAttribute("CURRENT_USER", user);
            return "redirect:/home";
        } else {
            return "login?error=true";
        }
    }

    @GetMapping("/home")
    public String home(HttpSession session) {
        User user = (User) session.getAttribute("CURRENT_USER");
        if (user == null) {
            return "redirect:/login";
        }
        return "home";
    }
}
2.1.2通过Model存储Session属性

Spring MVC还支持通过Model对象存储Session属性,这种方式更加类型安全,且可以与Flash属性结合使用。示例代码:

@Controller
public class UserController {

    @PostMapping("/login")
    public String login(@RequestParam("username") String username,
                           @RequestParam("password") String password,
                           Model model) {
        // 验证用户
        User user = userService.validate(username, password);
        if (user != null) {
            model.addAttribute("CURRENT_USER", user);
            return "redirect:/home";
        } else {
            model.addAttribute("ERROR", "Invalid username or password");
            return "login";
        }
    }

    @GetMapping("/home")
    public String home(@SessionAttribute("CURRENT_USER") User user) {
        if (user == null) {
            return "redirect:/login";
        }
        return "home";
    }
}
2.1.3通过Flash属性传递数据

在重定向场景中,Flash属性提供了一种临时存储机制,数据仅在一次请求间有效。示例代码:

@Controller
public class UserController {

    @PostMapping("/login")
    public String login(@RequestParam("username") String username,
                           @RequestParam("password") String password,
                           RedirectAttributes redirectAttributes) {
        // 验证用户
        User user = userService.validate(username, password);
        if (user != null) {
            redirectAttributes.addFlashAttribute("CURRENT_USER", user);
            return "redirect:/home";
        } else {
            redirectAttributes.addFlashAttribute("ERROR", "Invalid username or password");
            return "redirect:/login";
        }
    }

    @GetMapping("/home")
    public String home(@SessionAttribute("CURRENT_USER") User user) {
        if (user == null) {
            return "redirect:/login";
        }
        return "home";
    }
}

2.2 Spring Security与HttpSession集成

Spring Security提供了更强大的Session管理功能,包括会话固定攻击防御、并发控制等。以下是Spring Security与HttpSession集成的配置方式:

2.2.1XML配置方式

在传统的XML配置中,可通过以下方式配置Session管理:

<http auto-config="true">
    <!-- 会话固定攻击防御 -->
    <session Fixation-protection="migrateSession"/>
    <!-- 限制并发会话 -->
    <concurrent-session-control max-sessions="1"
                                       exception-if-maximum-exceeded="true"/>
    <!-- 设置会话超时 -->
    <session-management>
        <session Fixation-protection="migrateSession"/>
        <session-timeout>30</session-timeout> <!-- 单位:分钟 -->
    </session-management>
</http>

注意事项:XML配置方式在Spring Security 6.x中已逐渐被Java配置取代,建议新项目使用Java配置方式。

2.3 Flash属性的使用与原理

Flash属性是Spring MVC中一种特殊的Session属性,仅在一次请求间有效,特别适用于重定向场景。以下是Flash属性的使用方式:

2.3.1基本使用示例
@Controller
public class FlashController {

    @GetMapping("/form")
    public String formPage() {
        return "form";
    }

    @PostMapping("/submit")
    public String submitForm(@RequestParam("data") String data,
                                  RedirectAttributes redirectAttributes) {
        // 添加Flash属性
        redirectAttributes.addFlashAttribute("flashData", data);
        return "redirect:/result";
    }

    @GetMapping("/result")
    public String resultPage(@SessionAttribute("flashData") String data,
                                  Model model) {
        // 从Flash属性中获取数据
        model.addAttribute("flashData", data);
        return "result";
    }
}

2.3.2Flash属性的工作原理

Flash属性的工作原理基于以下机制:

  1. 存储阶段:当使用RedirectAttributes.addFlashAttribute()方法时,Spring会将属性存储到FlashMap中,并将FlashMap添加到当前请求的输出FlashMap中。
  2. 重定向阶段:在重定向过程中,Spring会将输出FlashMap中的属性转移到下一个请求的输入FlashMap中,并从Session中移除。
  3. 读取阶段:在下一个请求中,可通过FlashMap或Model获取这些属性,且不会在Session中长期保留。

实现原理:Flash属性通过FlashMapManager管理,利用Session作为临时存储介质,确保属性仅在一次请求间有效。

3.源码深度解析

3.1DispatcherServlet处理请求流程

DispatcherServlet是Spring MVC的核心组件,负责处理所有HTTP请求。在请求处理过程中,DispatcherServlet会自动管理HttpSession,确保在需要时创建和维护会话。

3.1.1请求处理流程源码分析

以下是DispatcherServlet处理请求的核心流程:

// DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
        throws Exception {

    // 1. 获取Handler(即Controller方法)
    HandlerExecutionChain handler = getHandler(request);
    if (handler == null) {
        noHandlerFound(request, response);
        return;
    }

    // 2. 获取HandlerAdapter(适配器模式)
    HandlerAdapter ha = getHandlerAdapter(handler);

    // 3. 执行Handler(调用Controller方法)
    ModelAndView mav = ha.handle(request, response, handler.getHandler());

    // 4. 处理视图渲染
    processDispatchResult(request, response, handler,av);
}

关键点:在ha.handle()方法中,Spring MVC会自动将HttpSession注入到Controller方法参数中,如果方法参数中有HttpSession类型,则会从request中获取并注入。

3.2HandlerAdapter与HttpSession的交互

HandlerAdapter是Spring MVC中负责调用Controller方法的核心组件。在调用Controller方法时,HandlerAdapter会自动解析方法参数并注入HttpSession

3.2.1HandlerAdapter源码分析

以下是HandlerAdapter接口的定义:

public interface HandlerAdapter {
    boolean supports(Object handler);
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception;
    long getLastModified(HttpServletRequest request, Object handler);
}

对于Controller方法,Spring MVC使用RequestMappingHandlerAdapter实现:

// RequestMappingHandlerAdapter#handle
@Override
public final void handleIntenal
        (HttpServletRequest request, HttpServletResponse response,
         HandlerMethod handlerMethod) throws Exception {

    // 1. 检查是否需要同步会话
    if (this.synchronizeOnSession) {
        HttpSession session = request sessions(false);
        if (session != null) {
            // 为当前会话获取互斥锁
            Object mutex = WebUtils sessionsMutex(session);
            synchronized (mutex) {
                // 调用方法参数解析器
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        } else {
            // 无会话则直接调用
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    } else {
        // 直接调用
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }

    // 其他处理...
}

关键点:在调用Controller方法前,Spring MVC会检查是否需要同步会话(通过synchronizeOnSession配置)。如果需要,会获取会话锁,确保同一会话中的请求顺序执行。

3.2.2参数解析器解析HttpSession

在解析Controller方法参数时,Spring MVC使用HandlerMethodArgumentResolver接口的实现类来处理各种参数类型。对于HttpSession参数,由SessionAttributeMethodArgumentResolver处理:

// SessionAttributeMethodArgumentResolver#resolveArgument
@Override
public Object resolveArgument(MethodParameter parameter,
                                       ModelAndViewContainer mavContainer,
                                       NativeWebRequest webRequest,
                                       WebDataBinderFactory binderFactory) throws Exception {

    // 1. 检查参数类型是否为HttpSession
    if (!HttpSession.class.isAssignableFrom (parameter.getParameterType())) {
        return null;
    }

    // 2. 从NativeWebRequest获取HttpSession
    HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
    if (request == null) {
        return null;
    }

    // 3. 返回HttpSession对象
    return request sessions();
}

关键点:SessionAttributeMethodArgumentResolver通过检查参数类型是否为HttpSession,决定是否从NativeWebRequest中获取并注入HttpSession对象。

3.3FlashMapManager实现原理

FlashMapManager是Spring MVC中管理Flash属性的核心组件,负责将属性从一个请求传递到下一个请求,并在之后自动清除。

3.3.1FlashMapManager接口定义
public interface FlashMapManager {
    @Nullable FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);
    void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
}
3.3.2FlashMap实现类

FlashMap是存储Flash属性的容器类,实现了 comparable接口以确定哪个FlashMap最匹配当前请求:

public final class FlashMap extends HashMap<String, Object>
        implements comparable<FlashMap> {

    // 目标请求路径
    @Nullable private String targetRequestPath;

    // 目标请求参数
    private final MultiValueMap<String, String> targetRequestParams = new LinkedMultiValueMap<>(3);

    // 过期时间戳
    private long expirationTime = -1;

    // 设置过期时间
    public void startExpirationPeriod(int timeToLive) {
        this.expirationTime = System.currentTimeMillis() + timeToLive * 1000;
    }

    // 判断是否过期
    public boolean isExpired() {
        return (this.expirationTime != -1 && System.currentTimeMillis() > this.expirationTime);
    }

    // 比较方法,确定哪个FlashMap更匹配当前请求
    @Override
    public int.compareTo(FlashMap other) {
        // 实现逻辑...
    }
}
3.3.3FlashMapManager工作流程

FlashMapManager的工作流程如下:

  1. 存储阶段

    • 当使用RedirectAttributes.addFlashAttribute()时,属性被添加到FlashMap中。
    • FlashMap被保存到当前请求的输出FlashMap中。
  2. 重定向阶段

    • 在重定向过程中,FlashMap被转移到下一个请求的输入FlashMap中。
    • FlashMap被保存到Session中,并设置过期时间。
  3. 读取阶段

    • 在下一个请求中,FlashMap被从Session中读取并添加到Model中。
    • FlashMap在读取后被标记为已处理,并在之后的请求中被清除。

以下是FlashMapManager的实现类(以RedisFlashMapManager为例)的源码片段:

// RedisFlashMapManager#saveOutputFlashMap
public void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request,
                                     HttpServletResponse response) {
    // 1. 生成唯一标识符
    FlashMapKey key = FlashMapKey.fromRequest(request);

    // 2. 设置过期时间
    flashMap.start expirationPeriod(flashMap.getFlashMapKey().getMaxWait());

    // 3. 保存到Redis
    redisTemplate.opsForValue().set(
            FlashMapRedisKey闪存(key),
            flashMap,
            flashMap.getFlashMapKey().getMaxWait(),
            TimeUnit.SECONDS
    );
}

// RedisFlashMapManager#retrieveAndUpdate
public FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) {
    // 1. 从请求中提取FlashMapKey
    FlashMapKey key = FlashMapKey.fromRequest(request);

    // 2. 从Redis中获取FlashMap
    FlashMap flashMap = redisTemplate.opsForValue().get(
            FlashMapRedisKey闪存(key)
    );

    // 3. 清除已处理的FlashMap
    if (flashMap != null) {
        redisTemplate.delete(FlashMapRedisKey闪存(key));
        flashMap.setFlashMapKey(key);
        flashMap.start expirationPeriod(flashMap.getFlashMapKey().getMaxWait());
    }

    return flashMap;
}

关键点:在分布式环境中,FlashMapManager可以通过Redis等外部存储实现Flash属性的共享,确保在集群中的多个服务器间正确传递Flash属性。

4.高级用法与最佳实践

4.1分布式环境下的Session共享方案

在分布式系统中,单机Session管理无法满足需求,因为用户可能在不同服务器间跳转。以下是几种主流的分布式Session共享方案:

4.1.1Session复制

实现原理:将Session数据复制到集群中的所有服务器,确保任何服务器都能处理用户请求。

配置示例(Tomcat集群):

<Cluster className="org.apache.catalina集群通道">
    <Manager className="org.apache.catalina集群会话管理器"
              maxIdleSwap="3600"
              minIdleSwap="1800"
              swapOnUpdate="false"/>
</Cluster>

优缺点

  • 优点:实现简单,无需额外组件。
  • 缺点:网络开销大,不适合大规模集群。
4.1.2Session粘滞

实现原理:通过负载均衡器(如Nginx)将同一用户的请求始终路由到同一服务器。

配置示例(Nginx):

http {
    upstream appcluster {
        ip_hash;
        server 192.168.1.101:8080;
        server 192.168.1.102:8080;
        server 192.168.1.103:8080;
    }

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://appcluster;
        }
    }
}

优缺点

  • 优点:实现简单,性能好。
  • 缺点:负载不均衡,单点故障风险。
4.1.3Spring Session与Redis集成

实现原理:使用Spring Session将Session数据存储到Redis中,实现分布式共享。

配置步骤

  1. 添加依赖:
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
    <version>3.3.0</version>
</dependency>
  1. 配置Redis连接:
spring session store-type=redis
spring redis host=127.0.0.1
spring redis port=6379
spring redis password=
  1. 配置SessionRepositoryFilter:
@Configuration
@EnableRedisHttpSession
public class SessionConfig {
    @Bean
    public RedisOperationsSessionRepository sessionRepository(
            RedisConnectionFactory connectionFactory) {
        RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(connectionFactory);
        sessionRepository.setSessionCookieDomain(null); // 设置Cookie域
        sessionRepository.setSessionCookiePath("/"); // 设置Cookie路径
        return sessionRepository;
    }
}

优缺点

  • 优点:实现简单,支持高并发,适合大规模集群。
  • 缺点:依赖Redis性能,配置复杂度较高。

4.2Session并发控制与超时处理

4.2.1并发控制配置

Spring Security提供了并发控制功能,限制同一用户同时登录的最大会话数。配置示例:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

    @Bean
    public SpringSessionBackedSessionRegistry sessionRegistry(
            RedisIndexedSessionRepository sessionRepository) {
        return new SpringSessionBackedSessionRegistry(sessionRepository);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/login**", "/css/**", "/js/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/home")
                .permitAll()
                .and()
            .logout()
                .permitAll()
                .and()
            .sessionManagement()
                .maximumSessions(1) // 最大会话数
                .sessionRegistry(sessionRegistry()) // 注册SessionRegistry
                . expiredUrl("/session Expired") // 会话过期跳转URL
                . exceptionIfMaximumExceeded(true); // 超过最大会话数时抛出异常
    }
}
4.2.2会话超时处理

Spring提供了多种方式设置会话超时时间:

  1. 通过web.xml配置
<session-config>
    <session-timeout>30</session-timeout> <!-- 单位:分钟 -->
</session-config>
  1. 通过Spring Security配置
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .sessionManagement()
                .sessionTimeout(30, TimeUnit.MINUTES) // 设置会话超时时间
                . expiredUrl("/session Expired"); // 设置会话过期跳转URL
    }
}
  1. 通过编程方式设置
// 在Controller方法中设置
session.setMaxInactiveInterval(1800); // 单位:秒

会话过期处理:当会话过期时,Spring会将用户重定向到配置的 expiredUrl,并清除会话中的属性。

4.3Session安全策略与防御措施

4.3.1防御Session固定攻击

Spring Security提供了Session固定攻击防御策略,包括:

  1. 迁移会话(migrateSession,默认):在用户登录后创建新会话,并将旧会话中的属性复制到新会话中。
  2. 新会话(newSession):在用户登录后创建新会话,但不复制旧会话中的任何属性。
  3. 无保护(none):不采取任何防御措施,保留原有会话。

配置示例:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .sessionManagement()
                .sessionFixation().migrateSession() // 默认策略
                .and()
                .maximumSessions(1)
                . expiredUrl("/session Expired");
    }
}

防御原理:通过在登录时生成新的Session ID,防止攻击者预先创建会话并诱导用户使用该会话登录。

4.3.2防御XSS攻击

防御措施:设置JSESSIONID Cookie的HttpOnly属性,防止通过JavaScript访问Cookie。

配置示例:

@Configuration
public class SessionConfig implements WebApplicationInitializer {

    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setHttpOnly(true); // 设置HttpOnly属性
        return serializer;
    }

    @Bean
    public SessionRepositoryFilter<RedisSession> sessionRepositoryFilter(
            SessionRepository<RedisSession> sessionRepository) {
        SessionRepositoryFilter<RedisSession> filter = new SessionRepositoryFilter<>(sessionRepository);
        filter.setCookieSerializer(cookieSerializer());
        return filter;
    }
}

4.3.3防御CSRF攻击

防御措施:设置JSESSIONID Cookie的Secure属性,仅通过HTTPS传输Cookie。

配置示例:

@Configuration
public class SessionConfig implements WebApplicationInitializer {

    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setSecure(true); // 设置Secure属性
        return serializer;
    }

    @Bean
    public SessionRepositoryFilter<RedisSession> sessionRepositoryFilter(
            SessionRepository<RedisSession> sessionRepository) {
        SessionRepositoryFilter<RedisSession> filter = new SessionRepositoryFilter<>(sessionRepository);
        filter.setCookieSerializer(cookieSerializer());
        return filter;
    }
}

5.常见问题与解决方案

5.1Session丢失问题排查与修复

5.1.1问题表现

用户登录后,在跳转到其他页面时提示未登录,Session属性丢失。

5.1.2可能原因
  1. Session未正确创建:未调用request sessions()或request sessions(true)方法创建Session。
  2. Session超时:Session在用户不活动期间过期。
  3. 分布式环境Session不同步:在集群环境中,Session未正确共享。
  4. Cookie未正确传递:浏览器未正确设置或传递JSESSIONID Cookie。
5.1.3解决方案
  1. 检查Session创建:确保在需要时调用request sessions(true)创建Session。
// 显式创建Session
HttpSession session = request sessions(true);
  1. 延长Session超时时间:通过配置或编程方式延长Session超时时间。
// 配置文件中设置
server.servlet.session.timeout=60m

// 编程方式设置
session.setMaxInactiveInterval(3600); // 单位:秒
  1. 配置分布式Session共享:在集群环境中,配置Session共享机制。
// Spring Session与Redis集成配置
@Configuration
@EnableRedisHttpSession
public class SessionConfig {
    @Bean
    public RedisOperationsSessionRepository sessionRepository(
            RedisConnectionFactory connectionFactory) {
        RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(connectionFactory);
        sessionRepository.setSessionCookieDomain(null);
        sessionRepository.setSessionCookiePath("/");

        // 设置Session超时时间
        sessionRepository.setSessionTimeout(3600000L); // 单位:毫秒

        return sessionRepository;
    }
}
  1. 检查Cookie设置:确保JSESSIONID Cookie正确设置并传递。
// 设置Cookie属性
Cookie cookie = new Cookie("JSESSIONID", session.getId());
cookie.setSecure(true);
cookie.setHttpOnly(true);
cookie.setPath(request上下文Path());
response.addCookie(cookie);

5.2Session性能优化策略

5.2.1减少Session数据量

优化策略:将大型数据(如文件、图片)存储到数据库或文件系统,而不是Session中。

// 存储文件路径到Session,而不是文件内容
session.setAttribute("FILE_PATH", "/uploads/file1.pdf");
5.2.2使用缓存优化Session访问

优化策略:在分布式环境中,使用缓存(如Redis)优化Session访问,减少数据库查询。

// 使用Redis缓存Session
@Configuration
@EnableRedisHttpSession
public class SessionConfig {
    @Bean
    public RedisOperationsSessionRepository sessionRepository(
            RedisConnectionFactory connectionFactory) {
        RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(connectionFactory);
        sessionRepository.setSessionCookieDomain(null);
        sessionRepository.setSessionCookiePath("/");

        // 设置缓存时间
        sessionRepository.setSessionTimeout(3600000L); // 单位:毫秒

        return sessionRepository;
    }
}
5.2.3设置合理的Session超时时间

优化策略:根据应用需求设置合理的Session超时时间,避免过早过期或长期占用内存。

// 配置文件中设置
server.servlet.session.timeout=30m

// 编程方式设置
session.setMaxInactiveInterval(1800); // 单位:秒

5.3跨域场景下的Session传递方案

5.3.1问题表现

在跨域请求中,浏览器默认不会发送Cookie,导致Session无法传递。

5.3.2解决方案
  1. 配置CORS允许Cookie:在Spring Security中配置CORS,允许发送Cookie。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/login**", "/css/**", "/js/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/home")
                .permitAll()
                .and()
            .logout()
                .permitAll()
                .and()
            .cors()
                .configurationSource(request -> {
                    CORSConfiguration config = new CORSConfiguration();
                    config.setAllowCredentials(true); // 允许凭证
                    config.setAllowedOrigins(Arrays.asList("http://client.example.com"));
                    config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
                    config.setAllowedHeaders(Arrays.asList("*"));
                    return config;
                });

    }
}
  1. 使用Token替代Session:在跨域场景中,使用JWT等Token替代Session,实现无状态认证。
// 生成JWT Token
String token = Jwts.builder()
        .setSubject(user.getUsername())
        .setIssuedAt(new Date())
        .setExpiration(new Date(System.currentTimeMillis() + 3600000))
        .signWith(SignatureAlgorithm.HS256, "secret").compact();

// 将Token返回给客户端
response.addHeader("Authorization", "Bearer " + token);
  1. 设置Cookie域:在分布式环境中,设置Cookie域,确保Session ID在所有子域中有效。
// 设置Cookie域
cookie.setDomain(".example.com");

网站公告

今日签到

点亮在社区的每一天
去签到