shiro源码分析

接上一篇,https://blog.csdn.net/luxinghong199106/article/details/105707188

这一篇主要通过2个流程,一个登录流程和一个登陆成功后的访问流程来看下 subject 的创建过程。因为subject 是 shiro 的核心,搞懂了它的创建过程,基本就搞懂了 shiro 的核心逻辑。(这一部分代码是基于传统表单验证的,不是基于JWT的)

1. 登录流程

    从前两篇得知,每次请求 shiro 都会创建一个 subject。我们直接从 shiroFilter 的 doFilterinternal() 开始

   

step into ,最终进到了 org.apache.shiro.mgt.DefaultSecurityManager#createSubject(org.apache.shiro.subject.SubjectContext)

这个方法的大概逻辑是“启发式”的从 SubjectContext 中获取一些数据,用来构造 subject,构造成功后保存 subject。

SubjectContext 本质上就是一个Map

用来存放跟 subject 相关的一些数据,上下文嘛,差不多多这个意思。

我们重点关注2步:

org.apache.shiro.mgt.DefaultSecurityManager#resolveSession和 org.apache.shiro.mgt.DefaultSecurityManager#resolvePrincipals

一个是解析session,一个是解析 principals。这2个都是 subject 中最重要的东西。

此时 SubjectContext 中只有 3个 item,request、response、securityManager

先看resolveSession,step into

先进入第一个红框

getSession() 就是从 context 中找 key 为 org.apache.shiro.subject.support.DefaultSubjectContext.SESSION ,类型为 org.apache.shiro.session.Session 的项,显然,此时context 中没有,返回null。

接着从context中找key为org.apache.shiro.subject.support.DefaultSubjectContext.SUBJECT,类型为org.apache.shiro.subject.Subject的项,其实就是看有没有已存在的 subject,有的话直接从里面拿session。显然,此时也没有,返回null。

第一个红框部分就结束了,没找到,进入第二个红框部分。

它的注释意思是,从context中不能直接解析出session,看看能不能从 sessionManager 中解析。

step into

getSessionKey 先从context中找key为org.apache.shiro.subject.support.DefaultSubjectContext.SESSION_ID的项作为sessionId,再加上request和response,构造出一个 WebSessionKey。显然,此时context中不存在sessionId,所以此时构造出来的 WebSessionKey 中的 sessionId 为 null。

继续往下走,到了 getSession(key),step into

调用 sessionManager 的 getSession(key),step into

进到了 ServletContainerSessionManager#getSession,然后我们看到了熟悉的一句代码:request.getSession(false)

shiro 将 tomcat 的 request 封装了一下,我们继续 step into

一路跟进,最终进到了tomcat的源码

后面的代码不再继续跟进,它属于servlet容器的范畴

很显然,此时 request.getSession(false) 返回null,因为容器中还没有任何 session。

那么 DefaultSecurityManager#createSubject(SubjectContext) 中的 resolveSession(context) 就结束了,“启发式”并没有获取到任何有关于session 的东西,不管是从 context 中获取,还是通过request从容器中获取。

接下来到了 resolvePrincipals(context),step into

getPrincipals 跟前面一样,从context获取,查找key为 org.apache.shiro.subject.support.DefaultSubjectContext.PRINCIPALS 的项,显然此时没有。

然后它会分别从context中查找key为 AUTHENTICATION_INFO、SUBJECT、SESSION 的项,只要有一个存在,就从找到的对象中解析 principals。此时,上述key在 context 中都还不存在,所以最后返回null。

然后我们进入实际的 subject 的创建方法,此时 context 中还是一开始的3个key,并没有“启发式”的获得其他数据。

step into ,到了 DefaultWebSubjectFactory#createSubject

可以看到最后是new了一个 WebDelegatingSubject 对象,我们重点关注一下其中最关键的 principals、authenticated、session 3个属性。

其实它们的逻辑都跟前面的是一致的,先尝试从 context 中查找,有session的话再尝试从session中找。

因为是第一次登录,最后的值都是null或false。

至此 subject 已经构造完毕,后面的逻辑就跟第一篇讲的一样,因为/login路径配置的是 anon filter,所以直接放行,进入到我们的login方法中。

此处就是构造一个UsernamePasswordToken,然后调用 subject 的 login 方法,若没抛出任何异常,说明登录成功。

接下来我们看看登录过程做的一些事情,step into

再step into

当验证成功没有抛出异常后,会执行 createSubject(token, info, subject),为什么这里又要创建一个 subject 呢?

我们先不进去,先退回上一步看看这个创建出来的 subject 被用来干什么

可以看到这个新创建出来的 subject 里面有 principals 和 session,用这2个属性来设置之前我们创建的 subject,所以此时的 subject 就变成了已认证的了。(这一部分在第二篇详细讲到。)

回到 createSubject(token, info, subject),看看它还做了些什么,step into

可以看到新建了一个空的 SubjectContext,然后填充了 Authenticated、AuthenticationToken、AuthenticationInfo、SecurityManager、Subject 5个item

我们知道 context 被用来做 ”启发式“ 的 subject 创建,所以这些东西在后面肯定用得着,继续 step into

又回到了熟悉的 DefaultSecurityManager#createSubject(SubjectContext)

这一次看看”启发式“的解析 session 和 principals 跟之前会不会不一样。

代码就不再跟进了,这里口述一下。

resolveSession(context) 先从context中找session,没有;接着找subject,有了,然后调用subject.getSession(false),但是此时还没有session,返回null。

resolvePrincipals(context) 先从context 中 principals,没有;接着找 AuthenticationInfo,有了,然后调用 info.getPrincipals() 得到 principals。

然后又进入实际的创建过程 doCreateSubject(context)

又重复了一遍上述的过程,最终创建出已认证的 subject 

此时进入了最后最重要的一块

step into

进入到了 DefaultSubjectDAO 的 save 方法,若 isSessionStorageEnabled ,则执行 saveToSession(subject),把当前subject保存到session。这里叉一点,在基于 jwt 的应用中,一般把 isSessionStorageEnabled 设为 false,表示不采用session来保存 subject 。

继续 step into

可以看到它分为了两块,principals 和 authenticationState 

先看第一块

先拿到 currentPrincipals,再通过 subject.getSession(false) 拿 session,明显此时没有,所以进入了红框逻辑。

它的意思是当前请求没有会话,但是已经认证了,怎么办呢?肯定要为它创建一个会话嘛。

所以我们看到了 session = subject.getSession() ,这句大家都很熟悉了吧,类似于 request.getSession(true),这一块底层又是tomcat的东西。创建完session后,将 currentPrincipals 设到 session 里。

同理,第二块会将  authenticated 设到 session里,它的值为true。

至此,我们的session里就有了 principals 和 authenticated 2个属性,到这里便恍然大悟了,最终shiro是通过这2个属性来维护用户状态的。

接下来我们进入第二个流程,验证一下上面说的,登陆成功后访问一个受保护路径看看 subject 的创建过程。

完整流程不再贴,直接贴关键不同的部分

在从 context 解析 session 的时候,context 中没找到(因为默认一开始只有3个key,上面有讲到),从request.getSession(false)来找,此时有了。因为这次我们的请求是带着 sessionId 的 cookie 过来的,tomcat 通过这个sessionId 找到了关联的 session。我们可以再看下这个session里有什么东西,有没有上面我们设置进去的2个属性

果然有!

resolveSession(context) 这一步成功解析出了session,并把session设到了 SubjectContext 中,用于后续的”启发式“创建。

同理, resolvePrincipals(context) 也成功解析到了,它是从session中解析出来的。

最后创建出了一个已认证的 subject,所以简单来说,Shiro 是基于 filter,然后代理出自己的 filter 链,为每个请求创建一个 subject,基于session 来维护 会话状态。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值