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. 检查参数类型是否为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的工作流程如下:
存储阶段:
- 当使用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. 保存到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中,实现分布式共享。
配置步骤:
- 添加依赖:
<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 {
@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提供了多种方式设置会话超时时间:
- 通过web.xml配置:
<session-config>
<session-timeout>30</session-timeout> <!-- 单位:分钟 -->
</session-config>
- 通过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
}
}
- 通过编程方式设置:
// 在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 {
@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可能原因
- 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 {
@Bean
public 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 {
@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解决方案
- 配置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;
});
}
}
- 使用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");