栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

shiro 重定向携带 JSESSIONID 问题解决

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

shiro 重定向携带 JSESSIONID 问题解决

问题描述: 环境

Springboot + shiro

前情

shiro 配置好后,登录成功后会生成 auth_token 保存并返回给前端 ,客户端往后每次发送请求都携带 auth_token 在请求头中,以此来判断登录状态。当请求不携带 auth_token 时判断为没登录,会跳转至提醒用户登录的接口(假设为 “/unauthorized”)。

问题发生过程

当发送请求时,携带一个错误的 auth_token 请求头,此时不会跳转,后台会出现 ”shiro Enabling session validation scheduler…“ 的问题。

问题排查

经过排查发现,下面是我自己写的 SessionManager ,用来判断请求是否携带了 “auth_token” 这个 token信息 ,如果携带了则把该 auth_token 返回交给 shiro 底层判断跟之前登录保存的 auth_token 是否一致,不一致则跳转到 “/unauthorized” 接口。

public class CustomWebSessionManager extends DefaultWebSessionManager {
    public static final String AUTH_HEAD_TOKEN = "auth_token";

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        if (request instanceof HttpServletRequest) {
            HttpServletRequest req = (HttpServletRequest) request;
            System.out.println(req.getRequestURL());
            String sessionId = req.getHeader(AUTH_HEAD_TOKEN);
            if (!StringUtils.isEmpty(sessionId)) {
                return sessionId;
            }
        }
        return super.getSessionId(request, response);
    }
}

问题就出现在这里,我输入了错误的 auth_token, shiro 底层判断必然失败,此时内部需要重定向到 “/unauthorized” 接口,然后重定向时会生成了新的 JSESSIONID,变成了 “/unauthorized;JSESSIONID=xxxxxxxx” ,这样服务器会判断这是一个新的请求,又会被 CustomWebSessionManager 拦截并重新判断是否携带了 “auth_token” 信息,然后shiro 底层又判断没有登录,内部需要再次重定向到 “/unauthorized” 接口,又生成了一个新的 JSESSIONID。形成一个死循环,当循环一定次数后,结束循环,后台就会出现 ”shiro Enabling session validation scheduler…“ 的问题。

简单来说就是,服务器第一次请求 “/unauthorized” 接口,但是携带的是错误的 JSESSIONID,所以判断是没有登录,然后再次请求 “/unauthorized” 接口,此时 JSESSIONID 发生了改变,导致服务器误以为是新的请求,又进行一次判断。

所以初步的解决方案有2个

  1. 重定向时生成的 JSESSIONID 不要发生改变
  2. 重定向时不携带 JSESSIONID
问题解决 方法一:

注: 没有重写过 getSessionId() 方法才行

通过查阅网上资料,得知 DefaultWebSessionManager 有个 sessionIdUrlRewritingEnabled 的配置,设置成false可以防止重定向携带 JSessionID

在 ShiroConfig 中

@Bean
public DefaultWebSessionManager sessionManager() {
    DefaultWebSessionManager defaultSessionManager = new CustomWebSessionManager();
    //将sessionIdUrlRewritingEnabled属性设置成false,可以防止重定向携带 JSessionID
    defaultSessionManager.setSessionIdUrlRewritingEnabled(false);
    return defaultSessionManager;
}


@Bean
public DefaultWebSecurityManager securityManager(DefaultWebSessionManager webSessionManager) {
    DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
    //设置websession对象
    defaultWebSecurityManager.setSessionManager(sessionManager());
    return defaultWebSecurityManager;
}

修改过后问题没有得到解决。认真思考后发现问题所在,我在自定义的 WebSessionManager 中重写了 getSessionId() 方法 ,所以才导致 sessionIdUrlRewritingEnabled 的配置失败了。

查看父级的 getSessionId() 方法:

private Serializable getReferencedSessionId(ServletRequest request, ServletResponse response) {
    String id = this.getSessionIdCookieValue(request, response);
    if (id != null) {
        request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "cookie");
    } else {
        id = this.getUriPathSegmentParamValue(request, "JSESSIONID");
        if (id == null && request instanceof HttpServletRequest) {
            String name = this.getSessionIdName();
            HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
            String queryString = httpServletRequest.getQueryString();
            if (queryString != null && queryString.contains(name)) {
                id = request.getParameter(name);
            }

            if (id == null && queryString != null && queryString.contains(name.toLowerCase())) {
                id = request.getParameter(name.toLowerCase());
            }
        }

        if (id != null) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "url");
        }
    }

    if (id != null) {
        request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
        request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
    }

    request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, this.isSessionIdUrlRewritingEnabled());
    return id;
}

通过阅读源码可以发现 getSessionId() 方法中 sessionIdUrlRewritingEnabled 的配置代码:

request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, this.isSessionIdUrlRewritingEnabled());

方法二:

自定义 WebSessionManager

public class CustomWebSessionManager extends DefaultWebSessionManager {
    public static final String AUTH_HEAD_TOKEN = "auth_token";

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        if (request instanceof HttpServletRequest) {
            HttpServletRequest req = (HttpServletRequest) request;
            System.out.println(req.getRequestURL());
            String sessionId = req.getHeader(AUTH_HEAD_TOKEN);
            if (!StringUtils.isEmpty(sessionId)) {
           // 禁止在url上拼接 JSessionId
            	request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, this.isSessionIdUrlRewritingEnabled());
            	return sessionId;
            }
        }
        return super.getSessionId(request, response);
    }
}
总结

当没有自定义 WebSessionManager重写 getSessionId() 方法时,调用的是父级的方法,使用方法一也可以,但是如果自己重写了 getSessionId() 方法,加入自己的逻辑判断,则需要使用方法二设置。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/992147.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号