Spring MVC中HttpSession的详解
1.HttpSession基础概念与作用
1.1 会话管理的核心需求
在Web应用中,HTTP协议本身是无状态的,这意味着服务器无法直接识别用户身份或保持用户状态。每次请求都独立于之前的请求,这给需要用户状态的应用(如登录验证、购物车)带来了挑战。为了解决这一问题,会话管理(Session Management)成为Web开发中的关键技术。
会话管理的核心需求包括:
- 用户身份识别:能够将用户与特定的会话关联起来,即使用户在多个页面间跳转。
- 状态保持:在不同请求之间保持用户状态,如购物车内容、用户偏好设置等。
- 并发控制:管理同一用户在不同设备上的多个会话,防止会话劫持。
- 安全性:防止会话固定攻击、XSS攻击等安全威胁。
这些需求使得HttpSession成为Web应用中不可或缺的组件,它为开发者提供了一种在服务器端管理用户会话状态的机制。
1.2HttpSession与Cookie的协同机制
HttpSession的实现依赖于Cookie技术,两者协同工作以保持用户会话状态。HttpSession通过JSESSIONID这一特殊的Cookie标识用户会话,具体机制如下:
1.2.1Session ID的生成与传递
当用户第一次访问服务器时,如果请求需要会话,服务器会执行以下步骤:
- 生成唯一Session ID:服务器创建一个全局唯一的Session ID,通常使用加密算法生成,以防止预测。
- 创建HttpSession对象:服务器在内存中创建一个HttpSession对象,并与生成的Session ID关联。
- 设置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还可通过以下方式传递:
- URL重写:在URL中附加;jsessionid=...参数,适用于不支持Cookie的客户端。
- POST参数:在表单提交中包含jsessionid参数。
- 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属性的工作原理基于以下机制:
- 存储阶段:当使用RedirectAttributes.addFlashAttribute()方法时,Spring会将属性存储到FlashMap中,并将FlashMap添加到当前请求的输出FlashMap中。
- 重定向阶段:在重定向过程中,Spring会将输出FlashMap中的属性转移到下一个请求的输入FlashMap中,并从Session中移除。
- 读取阶段:在下一个请求中,可通过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. 检查参数类型是否为HttpSessionif (!HttpSession.class.isAssignableFrom (parameter.getParameterType())) {return null;}// 2. 从NativeWebRequest获取HttpSessionHttpServletRequest 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更匹配当前请求@Overridepublic int.compareTo(FlashMap other) {// 实现逻辑...}
}
3.3.3FlashMapManager工作流程
FlashMapManager的工作流程如下:
存储阶段:
- 当使用RedirectAttributes.addFlashAttribute()时,属性被添加到FlashMap中。
- FlashMap被保存到当前请求的输出FlashMap中。
重定向阶段:
- 在重定向过程中,FlashMap被转移到下一个请求的输入FlashMap中。
- FlashMap被保存到Session中,并设置过期时间。
读取阶段:
- 在下一个请求中,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. 保存到RedisredisTemplate.opsForValue().set(FlashMapRedisKey闪存(key),flashMap,flashMap.getFlashMapKey().getMaxWait(),TimeUnit.SECONDS);
}// RedisFlashMapManager#retrieveAndUpdate
public FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) {// 1. 从请求中提取FlashMapKeyFlashMapKey key = FlashMapKey.fromRequest(request);// 2. 从Redis中获取FlashMapFlashMap flashMap = redisTemplate.opsForValue().get(FlashMapRedisKey闪存(key));// 3. 清除已处理的FlashMapif (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中,实现分布式共享。
配置步骤:
- 添加依赖:
<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId><version>3.3.0</version>
</dependency>
- 配置Redis连接:
spring session store-type=redis
spring redis host=127.0.0.1
spring redis port=6379
spring redis password=
- 配置SessionRepositoryFilter:
@Configuration
@EnableRedisHttpSession
public class SessionConfig {@Beanpublic 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 {@Beanpublic SessionRegistry sessionRegistry() {return new SessionRegistryImpl();}@Beanpublic SpringSessionBackedSessionRegistry sessionRegistry(RedisIndexedSessionRepository sessionRepository) {return new SpringSessionBackedSessionRegistry(sessionRepository);}@Overrideprotected 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提供了多种方式设置会话超时时间:
- 通过web.xml配置:
<session-config><session-timeout>30</session-timeout> <!-- 单位:分钟 -->
</session-config>
- 通过Spring Security配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.sessionManagement().sessionTimeout(30, TimeUnit.MINUTES) // 设置会话超时时间. expiredUrl("/session Expired"); // 设置会话过期跳转URL}
}
- 通过编程方式设置:
// 在Controller方法中设置
session.setMaxInactiveInterval(1800); // 单位:秒
会话过期处理:当会话过期时,Spring会将用户重定向到配置的 expiredUrl,并清除会话中的属性。
4.3Session安全策略与防御措施
4.3.1防御Session固定攻击
Spring Security提供了Session固定攻击防御策略,包括:
- 迁移会话(migrateSession,默认):在用户登录后创建新会话,并将旧会话中的属性复制到新会话中。
- 新会话(newSession):在用户登录后创建新会话,但不复制旧会话中的任何属性。
- 无保护(none):不采取任何防御措施,保留原有会话。
配置示例:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected 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 {@Beanpublic CookieSerializer cookieSerializer() {DefaultCookieSerializer serializer = new DefaultCookieSerializer();serializer.setHttpOnly(true); // 设置HttpOnly属性return serializer;}@Beanpublic 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 {@Beanpublic CookieSerializer cookieSerializer() {DefaultCookieSerializer serializer = new DefaultCookieSerializer();serializer.setSecure(true); // 设置Secure属性return serializer;}@Beanpublic 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可能原因
- Session未正确创建:未调用request sessions()或request sessions(true)方法创建Session。
- Session超时:Session在用户不活动期间过期。
- 分布式环境Session不同步:在集群环境中,Session未正确共享。
- Cookie未正确传递:浏览器未正确设置或传递JSESSIONID Cookie。
5.1.3解决方案
- 检查Session创建:确保在需要时调用request sessions(true)创建Session。
// 显式创建Session
HttpSession session = request sessions(true);
- 延长Session超时时间:通过配置或编程方式延长Session超时时间。
// 配置文件中设置
server.servlet.session.timeout=60m// 编程方式设置
session.setMaxInactiveInterval(3600); // 单位:秒
- 配置分布式Session共享:在集群环境中,配置Session共享机制。
// Spring Session与Redis集成配置
@Configuration
@EnableRedisHttpSession
public class SessionConfig {@Beanpublic RedisOperationsSessionRepository sessionRepository(RedisConnectionFactory connectionFactory) {RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(connectionFactory);sessionRepository.setSessionCookieDomain(null);sessionRepository.setSessionCookiePath("/");// 设置Session超时时间sessionRepository.setSessionTimeout(3600000L); // 单位:毫秒return sessionRepository;}
}
- 检查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 {@Beanpublic 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解决方案
- 配置CORS允许Cookie:在Spring Security中配置CORS,允许发送Cookie。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected 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;});}
}
- 使用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);
- 设置Cookie域:在分布式环境中,设置Cookie域,确保Session ID在所有子域中有效。
// 设置Cookie域
cookie.setDomain(".example.com");