不使用Spring Security实现会话超时管理及用户移除方案问询
我正在开发一款简易聊天应用,未使用Spring Security。前端用户输入用户名后,由以下Controller处理:
@PostMapping("/addUser") public User addUser(@RequestBody String name, HttpServletRequest request) { String session = (String) request.getSession().getAttribute("sessionId"); System.out.println("Session id is " + session); User newUser = new User(name, session); userService.addUser(newUser); System.out.println(newUser); return newUser; }
我使用拦截器的preHandle方法为用户生成sessionId,代码如下:
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("Its working"); // TODO Auto-generated method stub if(request instanceof HttpServletRequest) { HttpServletRequest servletRequest = (HttpServletRequest) request; HttpSession session = servletRequest.getSession(); session.setAttribute("sessionId", session.getId()); System.out.println("Connected with session id : " + session.getAttribute("sessionId")); } return true; }
我希望实现当用户长时间未活跃时,结束其会话并将该用户从存储所有注册用户的ArrayList中移除。请问不使用Spring Security是否可以实现该功能?我曾尝试使用任务调度,但发现无法在其中调用HttpSession。
完全可以不依赖Spring Security实现该功能,核心思路是自己维护用户活跃状态和会话映射,结合定时任务完成清理。具体步骤如下:
1. 扩展User类,增加活跃时间字段
给User类添加lastActiveTime字段,用来记录用户最后一次活跃的时间戳:
public class User { private String name; private String sessionId; private long lastActiveTime; // 记录最后活跃时间戳 // 构造方法、getter/setter public User(String name, String sessionId) { this.name = name; this.sessionId = sessionId; this.lastActiveTime = System.currentTimeMillis(); // 初始化时设为当前时间 } // 更新活跃时间的方法 public void updateActiveTime() { this.lastActiveTime = System.currentTimeMillis(); } // getter方法 public long getLastActiveTime() { return lastActiveTime; } public String getSessionId() { return sessionId; } }
2. 维护全局会话映射
创建一个全局的线程安全映射,用来存储sessionId和对应的HttpSession,这样定时任务就能通过sessionId找到会话:
@Component public class SessionManager { // 线程安全的HashMap,存储sessionId -> HttpSession private final ConcurrentHashMap<String, HttpSession> sessionMap = new ConcurrentHashMap<>(); // 添加会话 public void addSession(String sessionId, HttpSession session) { sessionMap.put(sessionId, session); } // 根据sessionId获取会话 public HttpSession getSession(String sessionId) { return sessionMap.get(sessionId); } // 删除会话 public void removeSession(String sessionId) { sessionMap.remove(sessionId); } }
3. 更新拦截器,维护会话映射和用户活跃时间
修改拦截器的preHandle方法,每次用户请求时更新用户的活跃时间,并将会话存入全局映射:
@Component public class SessionInterceptor implements HandlerInterceptor { @Autowired private SessionManager sessionManager; @Autowired private UserService userService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("Its working"); HttpSession session = request.getSession(); String sessionId = session.getId(); session.setAttribute("sessionId", sessionId); // 将会话存入全局映射 sessionManager.addSession(sessionId, session); // 更新当前用户的活跃时间(如果用户已存在) User currentUser = userService.getUserBySessionId(sessionId); if (currentUser != null) { currentUser.updateActiveTime(); } System.out.println("Connected with session id : " + sessionId); return true; } }
注意:需要在UserService中实现getUserBySessionId方法,根据sessionId从用户列表中查找对应的用户。
4. 编写定时清理任务
使用Spring的@Scheduled注解实现定时任务,定期检查用户活跃时间,清理超时用户:
@Component public class UserCleanupTask { @Autowired private UserService userService; @Autowired private SessionManager sessionManager; // 超时时间,比如30分钟(单位:毫秒) private static final long TIMEOUT = 30 * 60 * 1000; // 每隔1分钟执行一次清理 @Scheduled(fixedRate = 60 * 1000) public void cleanupInactiveUsers() { long currentTime = System.currentTimeMillis(); // 获取用户列表(注意线程安全,建议用Collections.synchronizedList包装原ArrayList) List<User> users = userService.getAllUsers(); // 遍历用户,找出超时的用户 Iterator<User> iterator = users.iterator(); while (iterator.hasNext()) { User user = iterator.next(); if (currentTime - user.getLastActiveTime() > TIMEOUT) { // 销毁会话 HttpSession session = sessionManager.getSession(user.getSessionId()); if (session != null) { session.invalidate(); sessionManager.removeSession(user.getSessionId()); } // 从用户列表中移除 iterator.remove(); System.out.println("清理超时用户:" + user.getName()); } } } }
注意:要确保UserService中的用户列表是线程安全的,比如在初始化时用Collections.synchronizedList(new ArrayList<>())创建,避免并发修改问题。
5. 开启Spring定时任务支持
在Spring Boot启动类上添加@EnableScheduling注解,开启定时任务功能:
@SpringBootApplication @EnableScheduling public class ChatApplication { public static void main(String[] args) { SpringApplication.run(ChatApplication.class, args); } }
这样就能实现用户长时间未活跃时自动销毁会话并移除用户的功能,全程不需要依赖Spring Security。
内容的提问来源于stack exchange,提问作者LazyCoder

