最近在学习shiro的源码,所以记录下来自己的心得。
学习源码查看网上教程一直都是很迷,源码这种东西需要自己不断去定位去看才能看明白。
那么我们现在开始:这次我们主要讲解SecurityUtils.getSubject()并从这个方法入手看看到底怎么获取到我们要的subject。
这个类中只有一个securityManager对象,我们知道shiro所有的认证,授权,以及session管理都通过securityManager来进行控制,而SecurityUtils只是对其的在一层代理罢了。
从getSubject()我们能够看到,先从ThreadContext中拿取,如果没有则(new Subject.Builder()).buildSubject(); 并且绑定到当前线程中。
我们先假设当前线程中存在。这时候我们是怎么去获取我们要的subject呢。进入方法查看:
可以看出来,我们进入get(SUBJECT_KEY) 也就是get一个静态字符串。get方法中只是为了调用这一句Object value = getValue(key);
这里的key就是这个静态字符串。也就是说我们所有用户访问时候其实都只是get(SUBJECT_KEY),而这个getValue方法中是使用resouce的get方法。resouce是一个线程变量,也就是所有线程都维护自己的变量不共享且互斥的。
我们进入这个resource.get() 来看看
这里我们简单讲解一下ThreadLocal怎么维护各线程都只访问自己的变量不干扰,若想了解,可以深入去研究下。
首先获取当前线程,之后get(t)得到 t.threadLocals; 也就是一个ThreadLocalMap对象 这个ThreadLocalMap是ThreadLocal一个内部类
这个内部类中有一个Entry内部类 以及一个Entry[] table 数组,根据hash算法获取对应的value;那么问题来了
map.getEntry(this); 这个this指的是啥。 这个this指的就是1.1中的resource变量。
所以我们大概理解下就是,各线程去访问ThreadLocal对象 resouce (其实只有一个resouce)这时候进来先获取自己线程对象,然后在线程中get得到ThreadLocalMap,若存在则使用这个resouce作为key来找并使用hash算法来算,若不存在则初始化,并set一下。那么我们明白了我们登陆的Subject对象保存在哪里以及为什么多个用户访问能够分别区分,其实就是使用了线程变量。
/////////////////////////////////////////////////////////////我是分割线/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
接下来我们来看一下如果 在ThreadContext中没有获取到Subject会去哪里取呢?
进入new Subject.Builder() 以及buildSubject() 方法来看
this.subjectContext = newSubjectContextInstance();这个subject上下文就是一个map里面存放了相关信息
newSubjectContextInstance() =this.backingMap =newHashMap();也就是父类中的backingMap。
最后把securityManager设置进去也就是把这个安全管理器放到这个备份的map中。
然后开始创建subject把这个map传进去。
这就是第一次访问时候来创建一个subject方法的实现。 首先把这个map备份一下,采用它的副本context来处理。
之后来检测是否有securityManager,再来检测是否有session,再来检测是否能解析Principals 最后创建Subject 并且保存一下。
这个检测是否有session等等这些方法逻辑处理都是一样的,检测是否有,有就返回context没有就创建并加入到context中再返回,我们以session为例一些逻辑处理最后定位到
这就很明白了,session其实也就是得到httpServletRequest中的getSession();进行一层包装代理。所以到此Subject对象也就创建好了回到代码1.1我们再进行和当前线程绑定一下,则就缓存到当前线程变量resource中了,这样当我们随时想要获取Subject对象都可以获得这个Subject。
都是一个一个码的,不容易啊。希望能有所帮助,很多地方可能不足请多指教。