ApiController使用Session验证出现Null解决方案

解决Session丢失问题
本文介绍了一个在服务端保存登录信息时遇到的问题,即HttpContext.Current.Session为null的情况,并提供了解决方案,即在Global.asax中开启Session功能。

问题描述

在服务端保存登录信息,出现异常信息

image

分析发现HttpContext.Current.Session为null

解决方案

执行时出报异常,要在Global.asax里添加:开启Session功能

image

转载于:https://www.cnblogs.com/zsy/p/5219333.html

<think>在SSM项目中,根据Session ID获取并销毁Session需要解决两个核心问题: 1. 如何根据Session ID获取对应的HttpSession对象? 2. 如何安全地销毁指定Session? 参考引用[1]提到:由于`session.getSessionContext().getSession(sessionId)`方法已被废弃,我们可以自定义一个SessionContext来管理Session。同时,引用[3]展示了SSM项目中Session使用(登录和登出)以及通过`SessionStatus`来清除Session(但这种方式是针对当前会话的)。 因此,我们可以采用以下方案: 步骤1:自定义一个Session上下文管理器(MySessionContext),用于存储所有活跃的Session。 步骤2:通过HttpSessionListener监听器在Session创建和销毁时维护这个上下文。 步骤3:提供工具方法,根据Session ID获取Session并销毁。 具体实现如下: ### 1. 创建自定义Session上下文管理器 ```java import javax.servlet.http.HttpSession; import java.util.concurrent.ConcurrentHashMap; public class MySessionContext { private static final ConcurrentHashMap<String, HttpSession> SESSION_MAP = new ConcurrentHashMap<>(); public static void addSession(HttpSession session) { if (session != null) { SESSION_MAP.put(session.getId(), session); } } public static void removeSession(HttpSession session) { if (session != null) { SESSION_MAP.remove(session.getId()); } } public static HttpSession getSession(String sessionId) { return SESSION_MAP.get(sessionId); } } ``` ### 2. 实现Session监听器,维护上下文 ```java import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; @WebListener public class MySessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent se) { MySessionContext.addSession(se.getSession()); } @Override public void sessionDestroyed(HttpSessionEvent se) { MySessionContext.removeSession(se.getSession()); } } ``` ### 3. 创建工具类,提供根据Session ID销毁Session的方法 ```java import javax.servlet.http.HttpSession; public class SessionUtils { /** * 根据Session ID获取Session * @param sessionId Session ID * @return HttpSession对象,如果不存在则返回null */ public static HttpSession getSessionById(String sessionId) { return MySessionContext.getSession(sessionId); } /** * 根据Session ID销毁Session * @param sessionId Session ID */ public static void invalidateSession(String sessionId) { HttpSession session = getSessionById(sessionId); if (session != null) { session.invalidate(); // 使Session失效 } } } ``` ### 4. 在Controller中实现销毁指定SessionAPI ```java import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class SessionController { @GetMapping("/invalidate/{sessionId}") public String invalidateSession(@PathVariable String sessionId) { SessionUtils.invalidateSession(sessionId); return "Session " + sessionId + " has been invalidated."; } } ``` ### 5. 配置监听器(如果使用web.xml) 如果项目使用`web.xml`,需要添加监听器配置(若使用`@WebListener`注解且Servlet容器支持则无需配置): ```xml <web-app> <listener> <listener-class>com.example.MySessionListener</listener-class> </listener> </web-app> ``` ### 注意事项 1. **线程安全**:使用`ConcurrentHashMap`保证线程安全。 2. **Session失效**:调用`session.invalidate()`会触发`sessionDestroyed`事件,监听器会自动从上下文中移除该Session。 3. **分布式环境**:此方案仅适用于单机环境。在分布式系统(如集群)中,需要使用集中式Session管理(如Redis)并配合Spring Session等框架。 4. **安全性**:暴露销毁SessionAPI需谨慎,应添加权限控制(如管理员才能调用)。 ### 补充:如何获取Session ID? 在用户登录后,前端可以通过Cookie获取Session ID(通常名为`JSESSIONID`),或通过响应头获取。在服务端,可以通过`request.getSession().getId()`获取当前Session的ID。 ### 参考引用说明 - 引用[1]:自定义Session上下文来管理Session,替代废弃方法。 - 引用[3]:展示了SSM中Session的基本使用(登录登出),但登出方法仅清除当前Session,而我们需要根据Session ID清除指定Session。 §§相关问题§§ 1. SSM项目中如何实现Session的分布式管理(如使用Redis)? 2. 如何安全地在前端传递Session ID以避免安全风险? 3. 在SSM项目中,如何实现管理员强制用户下线功能?</think>### SSM项目中根据Session ID获取并销毁Session的实现方案 在SSM项目中,根据Session ID获取并销毁Session需要解决两个核心问题: 1. 如何根据Session ID获取对应的HttpSession对象 2. 如何安全地销毁指定Session 以下是完整的实现方案: #### 1. 创建Session上下文管理器(核心组件) ```java import javax.servlet.http.HttpSession; import java.util.concurrent.ConcurrentHashMap; public class MySessionContext { // 使用线程安全的ConcurrentHashMap存储所有活跃Session private static final ConcurrentHashMap<String, HttpSession> SESSION_MAP = new ConcurrentHashMap<>(); // 添加Session到上下文 public static void addSession(HttpSession session) { if (session != null) { SESSION_MAP.put(session.getId(), session); } } // 从上下文中移除Session public static void removeSession(HttpSession session) { if (session != null) { SESSION_MAP.remove(session.getId()); } } // 根据Session ID获取Session public static HttpSession getSession(String sessionId) { return SESSION_MAP.get(sessionId); } // 销毁指定Session public static void invalidateSession(String sessionId) { HttpSession session = SESSION_MAP.get(sessionId); if (session != null) { session.invalidate(); // 使Session失效 } } } ``` #### 2. 实现Session监听器(维护Session上下文) ```java import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; @WebListener public class SessionLifecycleListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent se) { // Session创建时添加到上下文 MySessionContext.addSession(se.getSession()); } @Override public void sessionDestroyed(HttpSessionEvent se) { // Session销毁时从上下文移除 MySessionContext.removeSession(se.getSession()); } } ``` #### 3. 在web.xml中注册监听器 ```xml <web-app> <listener> <listener-class>com.example.SessionLifecycleListener</listener-class> </listener> </web-app> ``` #### 4. 创建Session管理工具类 ```java import javax.servlet.http.HttpSession; public class SessionUtils { // 根据Session ID获取Session public static HttpSession getSessionById(String sessionId) { return MySessionContext.getSession(sessionId); } // 销毁指定Session public static void destroySession(String sessionId) { MySessionContext.invalidateSession(sessionId); } // 获取当前用户信息(示例) public static Object getCurrentUser(HttpSession session) { return session.getAttribute("user"); } } ``` #### 5. 在Controller中实现销毁功能 ```java import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class SessionController { // 根据Session ID销毁SessionAPI @GetMapping("/invalidate/{sessionId}") public String invalidateSession(@PathVariable String sessionId) { SessionUtils.destroySession(sessionId); return "Session " + sessionId + " has been invalidated"; } // 获取Session信息(示例) @GetMapping("/session/{sessionId}") public Object getSessionInfo(@PathVariable String sessionId) { HttpSession session = SessionUtils.getSessionById(sessionId); if (session == null) { return "Session not found or expired"; } return SessionUtils.getCurrentUser(session); } } ``` #### 6. 用户登录时设置Session属性(关键步骤) ```java @Controller @SessionAttributes("user") // 声明Session属性 public class LoginController { @PostMapping("/login") public String login(User user, Model model) { // 验证用户逻辑... model.addAttribute("user", user); // 将用户对象存入Session return "redirect:/dashboard"; } } ``` ### 实现原理说明 1. **Session跟踪机制**: - 通过`HttpSessionListener`监听所有Session生命周期事件 - 使用`ConcurrentHashMap`实时维护所有活跃Session的注册表 - 每个Session创建时自动注册,销毁时自动移除[^1] 2. **Session销毁过程**: - 调用`session.invalidate()`会触发Session销毁事件 - 监听器自动从注册表中移除该Session - Session关联的所有属性将被清除[^2] 3. **安全注意事项**: ```java // 在实际应用中应添加权限检查 @PreAuthorize("hasRole('ADMIN')") public void destroySession(String sessionId) { // 销毁逻辑 } ``` ### 常见问题解决方案 1. **Session获取为null的情况**: - 检查Session是否已过期(默认30分钟不活动会过期) - 确认Session ID是否正确传递 - 验证监听器是否正确注册 2. **分布式环境解决方案**: ```xml <!-- 在pom.xml中添加Spring Session依赖 --> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> ``` 使用Redis等分布式存储替代内存注册表 3. **Session固定攻击防护**: ```java // 用户认证成功后刷新Session ID request.changeSessionId(); ``` ### 关键引用说明 - 引用[1]:自定义Session上下文替代已废弃的`getSessionContext()`方法 - 引用[2]:Session底层依赖Cookie实现,浏览器关闭时Session失效 - 引用[3]:SSM中通过`@SessionAttributes`和`SessionStatus`管理Session - 引用[4]:跨域请求需特殊处理Session携带问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值