You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修改密码后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();     
    }       
}

关键改动说明

  1. 逻辑顺序调整:把密码修改后的logout判断逻辑移到intercept方法最开始,确保在执行原Action前就处理logout。
  2. 直接返回登录页:处理完logout后,直接返回LOGIN字符串,让Struts框架直接跳转到登录页面,不再继续执行原请求的Action逻辑。
  3. 拆分监听器逻辑:监听器中只保留设置passwordChanged标记的逻辑,避免混淆流程。

这样修改后,当用户修改密码后第一次发起请求时,拦截器会先检测到标记,执行logout并直接跳转登录页,无需二次请求。

内容的提问来源于stack exchange,提问作者Setareh Mokhtari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 14:07:33