Shiro会话管理/前后分离身份鉴别原理


我们常用 SecurityUtils.getSubject().getPrincipal();获取当前登录用户信息,但是这个方法是如何获得用户信息的?Shiro又是如何区分不同用户的身份的?

问题1. SecurityUtils.getSubject().getPrincipal()返回类型;

查看源码得知它是Object,但是实际上,他的返回类型由我们控制。

在Realm类中有个doGetAuthenticationInfo方法,我们常在这里进行登录逻辑处理,其返回类型是AuthenticationInfo,我们通常使用SimpleAuthenticationInfo,追进去可以看到其第一个参数就是principal,即我们的用户类型:
在这里插入图片描述
因此,如果我们最终

User user = new User();
user.setPassword("111");
user.setName("sxuer");
return new SimpleAuthenticationInfo(user, user.getPassword(), getName());

则SecurityUtils.getSubject().getPrincipal()将得到User类。

String user = "sxuer";
return new SimpleAuthenticationInfo(user, "123", user);

则SecurityUtils.getSubject().getPrincipal()将得到字符串。

问题2. Shiro如何利用session保持会话

先说结果:

  • 若使用Shiro默认的session会话管理:用户登录–>创建session–>创建cookie–>用户二次访问–>从cookie中读取sessionId–>根据sessionId取得用户身份
  • 若使用自定义的session会话管理,适用于前后分离(但需要解决跨域cookie禁止问题):用户登录,同时携带sId–>检查到sId参数,使用自定义getsessionId方法–>保存sessionId到cookie中–>用户二次访问,携带sessionId–>根据sessionId取得用户身份

上述两者的主要区别在于,第二种用户登录时,需要携带一个额外的参数,用于创建后续访问的sessionId。

原理

很容易得知,Shiro中有个会话管理器DefaultWebSessionManager,既然Shiro使用session保持会话,那么核心就在于session的创建以及获取(校验)。

DefaultWebSessionManager中有个getSessionId方法,向上追溯,可以发现Shiro会从request中查询cookie,如果找到了,那就作为sessionId保存–>可以得出,sessionId其实是由客户端控制的。如果从cookie中没找到,就从uri中读取JSESSIONID参数,如果依旧没有,就会抛出找不到的异常。

我们可以重写getSessionId方法,从而实现session的定制(若将cache换成redis,就跟传统上使用redis保存token的原理基本一致)。

假设重写的getSessionId如下:

protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        // 如果参数中包含“__sid”参数,则使用此sid会话。 例如:http://localhost/project?__sid=xxx&__cookie=true
        String sid = request.getParameter("__sid");
        if (StringUtils.isNotBlank(sid)) {
            // 是否将sid保存到cookie,浏览器模式下使用此参数。
            if (WebUtils.isTrue(request, "__cookie")) {
                HttpServletRequest rq = (HttpServletRequest) request;
                HttpServletResponse rs = (HttpServletResponse) response;
                Cookie template = getSessionIdCookie();
                Cookie cookie = new SimpleCookie(template);
                cookie.setValue(sid);
                cookie.saveTo(rq, rs);
            }
            // 设置当前session状态
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
                    ShiroHttpServletRequest.URL_SESSION_ID_SOURCE); // session来源与url
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sid);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return sid;
        } else {
            return super.getSessionId(request, response);
        }
    }

当用户登录时,携带了参数__sid=1,那么Shiro会利用该参数创建session,但是并非该值就是session

Shiro中有个类叫做SessionDao,显然是用于创建Session的,我们进入查看,会发现有个doCreate,阅读之后可以发现,他会利用sessionIdGenerator生成sessionId,然后使用assignSessionId(session, sessionId)将sessionId设置到session对象中。然后在Cache类中(如CachingSessionDao)以sessionId:session作为键值对保存。

获取sessionId:
从cookie中获取session
若为空,则创建session,保存到cookie中,保存到本地Cache中

相关方法:
getSessionId(ServletRequest request, ServletResponse response)
getSessionIdCookieValue(request, response)
readValue(HttpServletRequest request, HttpServletResponse ignored)
getCookie(request, name)
cookie.getValue();
request.setAttribute

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值