修改密码后Session失效,首次点击需两次跳转登录页的问题求助
问题:修改密码后Session失效但首次请求无法直接跳转登录页
设置passwordChanged标记后,首次点击请求时,authenticationServices.logout(getHttpServletRequest())会使Session失效,但必须再次请求(双击)才会跳转到登录页。需求是Session失效后首次点击请求就能直接跳转到登录页。
当前使用的拦截器代码
public class LogOutAfterChangePasswordInterceptor extends BaseInterceptor { private static final long serialVersionUID = 5906534052815899264L; private static final Logger LOG = LoggerFactory.getLogger(LogOutAfterChangePasswordInterceptor.class); private static final String ACTIONS_TO_REMEMBER = "static-password-setting-result"; private static final String PASSWORD_CHANGED_FLAG = "passwordChanged"; public static final String LOGIN = "login"; @Inject private AuthenticationServices authenticationServices; @Override public String intercept (ActionInvocation invocation) throws Exception { invocation.addPreResultListener((actionInvocation, resultCode) -> { HttpServletRequest request = getHttpServletRequest(); HttpSession session = request.getSession(false); if (getActionName().contains(ACTIONS_TO_REMEMBER)) { LOG.trace("push URL `{}` that user after that should be logout ", ACTIONS_TO_REMEMBER); session.setAttribute(PASSWORD_CHANGED_FLAG, true); } else { Boolean passwordChanged = session != null ? (Boolean) session.getAttribute(PASSWORD_CHANGED_FLAG) : null; if (passwordChanged != null && passwordChanged) { // Perform logout or other necessary actions session.removeAttribute(PASSWORD_CHANGED_FLAG); LOG.trace("User will be logged out after the next request"); try { authenticationServices.logout(getHttpServletRequest()); } catch (ClientException e) { // TODO Auto-generated catch block e.printStackTrace(); } // logout spring manually Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) { new SecurityContextLogoutHandler().logout(getHttpServletRequest(), getHttpServletResponse(),auth); } SecurityContextHolder.getContext().setAuthentication(null); } } }); return invocation.invoke(); } }
问题原因
当前代码把logout逻辑放在PreResultListener中执行,这个监听器是在Action执行完毕、准备返回结果前触发的。此时当前请求的响应流程已经启动,即使Session被失效,当前请求还是会按照原有逻辑返回响应,不会触发登录跳转,必须等下一次请求时,系统检测到Session失效才会跳转。
解决办法
将标记判断和logout逻辑移到intercept方法的最前端,在执行原Action请求之前就处理logout操作,处理完成后直接返回登录页的逻辑标识,终止原请求的执行流程。
修改后的代码
public class LogOutAfterChangePasswordInterceptor extends BaseInterceptor { private static final long serialVersionUID = 5906534052815899264L; private static final Logger LOG = LoggerFactory.getLogger(LogOutAfterChangePasswordInterceptor.class); private static final String ACTIONS_TO_REMEMBER = "static-password-setting-result"; private static final String PASSWORD_CHANGED_FLAG = "passwordChanged"; public static final String LOGIN = "login"; @Inject private AuthenticationServices authenticationServices; @Override public String intercept (ActionInvocation invocation) throws Exception { HttpServletRequest request = getHttpServletRequest(); HttpSession session = request.getSession(false); // 先处理密码修改后的logout逻辑,放在最前面优先执行 if (!getActionName().contains(ACTIONS_TO_REMEMBER)) { Boolean passwordChanged = session != null ? (Boolean) session.getAttribute(PASSWORD_CHANGED_FLAG) : null; if (passwordChanged != null && passwordChanged) { session.removeAttribute(PASSWORD_CHANGED_FLAG); LOG.trace("User is being logged out and will redirect to login page"); try { authenticationServices.logout(getHttpServletRequest()); } catch (ClientException e) { e.printStackTrace(); } // 手动处理Spring Security logout Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) { new SecurityContextLogoutHandler().logout(getHttpServletRequest(), getHttpServletResponse(), auth); } SecurityContextHolder.getContext().setAuthentication(null); // 直接返回登录页标识,终止原请求执行 return LOGIN; } } // 处理密码修改成功后设置标记的逻辑 invocation.addPreResultListener((actionInvocation, resultCode) -> { HttpSession preResultSession = request.getSession(false); if (getActionName().contains(ACTIONS_TO_REMEMBER)) { LOG.trace("push URL `{}` that user after that should be logout ", ACTIONS_TO_REMEMBER); preResultSession.setAttribute(PASSWORD_CHANGED_FLAG, true); } }); return invocation.invoke(); } }
关键改动说明
- 逻辑顺序调整:把密码修改后的logout判断逻辑移到
intercept方法最开始,确保在执行原Action前就处理logout。 - 直接返回登录页:处理完logout后,直接返回
LOGIN字符串,让Struts框架直接跳转到登录页面,不再继续执行原请求的Action逻辑。 - 拆分监听器逻辑:监听器中只保留设置
passwordChanged标记的逻辑,避免混淆流程。
这样修改后,当用户修改密码后第一次发起请求时,拦截器会先检测到标记,执行logout并直接跳转登录页,无需二次请求。
内容的提问来源于stack exchange,提问作者Setareh Mokhtari
相关产品推荐
相关产品推荐

