shiro工作原理(大概)

热身

首先说到shiro,大家都必须了解几个知识点: Filter、、InitializingBean、FactoryBean

① 在web服务启动时,首先会加载web.xml文件,对Filter、Servlety以及一些参数进行初始化,Filter会先执行init(FilterConfig config)方法进行初始化,其中的doFilter()方法会对其在web.xml文件中配置的路径进行过滤

在其过滤的过程中,可以根据自己的需求进行一些请求处理,整个web服务启动和访问过程中,init()方法只会执行一次,而doFilter()会执行多次,destroy()方法也是执行一次,在Filter实例销毁的时候执行。

② BeanPostProcessor这个接口类在Spring中会作为后置处理器的表示,如果一个类实现了这个接口类,那么在Spring容器的加载过程中,Spring会自动识别实现了PostBeanProcessor这个接口类的实现类,并且

或将这个实现类注册为后置处理器。PostBeanProcessor这个接口类中有两个抽象方法 :postProcessBeforeInitialization(Object bean, String beanName) 方法会在Spring容器中Bean被实例化之前执行,而

Object postProcessAfterInitialization(Object bean, String beanName)方法会在Spring容器中Bean被实例化之后执行,通常都是在postProcessBeforeInitialization(Object bean, String beanName)方法中针对Bean的实例化

做一些初始化操作。

③ InitializingBean 在Spring中也是一个接口类,凡是实现了这个接口类,都需要实现它的接口方法afterPropertiesSet(),并且实现类作为一个单例的Bean被实例化后会先用实现的afterPropertiesSet()方法,因此也可以在

通过实现 InitializingBean这个接口类来达到一些Bean被实例化之后的一些初始化操作,这个实现逻辑根据自己的需求而定。

④ FactoryBean 是Spring支持的工厂接口类,如果实现对FactoryBean 进行实现的话,那么在Spring容器加载的过程中,会针对该接口类的实现类进行判断,如果实现了该接口类,通常情况是通过实现方法getObject()方法来进行实例化,以及后续的一些操作。

 

开始

了解完以上的四个知识点,接下来再探索Shiro的运行流程以及通过它与Spring整合的时候,Shiro的相关Bean到底是怎样协作与运行的。

一、

首先看web.xml文件中配置的Shiro的过滤器配置

 

<filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
        <param-name>targetFilterLifecycle</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

 

<filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

 

进入.DelegatingFilterProxy 这个过滤器类中,我们可以看到DelegatingFilterProxy类继承了GenericFilterBean

GenericFilterBean 这个类是抽象类,并且该类实现了Filter接口类和 InitializingBean接口类,很明显,第一步就是直接找到实现的 init(FilterConfig config)方法,但是这个方法的实现

是在GenericFilterBean中,在该方法中对web.xml文件中配置的Filter进行过滤,将各个Filter配置的参数存储到容器中每个Filter对应的FilterConfig中,方便后面获取Filter的相关信息,接着这个init()方法继续看,不难发现其中调用了

initFilterBean()方法 ,然后此时调用的initFilterBean()是DelegatingFilterProxy实现的方法,再进入DelegatingFilterProxy中的initFilterBean()方法中,

 

 

根据initFilterBean()的实现中可以发现其参数targetBeanName只会被初始化一次,一旦不为null,就不会被重新赋值。 getFilterName()返回的就是当前加载的Filter过滤器的名称,也就是web.xml文件中配置的 ShiroFilter,并且会通过web环境

对 delegate 进行初始化。

接着再看GenericFilterBean中针对 InitializingBean 的实现,

会发现,在afterPropertiesSet()方法中调用的是DelegatingFilterProxy类中的initFilterBean()方法。到此时,针对web容器对ShiroFilter的加载顺序大概了解了。

 

二、

 接下来从Shiro与Spring整合的配置来进行分析。

使用过Shiro的人大概都知道LifeCycleBeanPostProcessor 这个类管理Shiro中各个Bean的生命周期。话不多说,直接看源码:

从上面截图我们可以看到 LifeCycleBeanPostProcessor这个类间接实现了BeanPostProcessor这个接口类,因此Spring容器加载的时候也会自动把 LifeCycleBeanPostProcessor这个类

注册为后置处理器,看看 LifeCycleBeanPostProcessor中针对 BeanPostProcessor接口类的实现:

由上图可以知道在每个Bean实例化之前都会判断一下 是不是 Initializable接口类型的实例,如果是,就调用其实现的init()方法,通过源码发现:

AuthorizingRealm 类 实现了Initializable接口类,并且MyRealm继承了AuthorizingRealm,(这里声明一下:MyRealm是 自定义认证和授权的Realm类)

 因此就能知道这里主要是针对自定义的MyRealm类进行过滤的,一旦满足,则会调用其init()方法:

init()方法的实现中主要是针对缓存的获取,因为在这个Shiro整合Spring过程中,我没有配置CacheManager,因此这一步走可以先忽略,感兴趣的可以自己配置好CacheManager缓存管理器之后再仔细跟踪。

 

接着我们再看 MethodInvokingFactoryBean,其参数staticMethod 和arguments指定了SecurityUtils的setSecurityManager()方法和SecurityManager的引用。

 

由此可以知道为什么能指定那两个参数了,从上面几个图中可以知道MethodInvokingFactoryBean间接实现了 InitializingBean 和FactoryBean这两个接口类,因此直接找到

getObject()方法的实现和afterPropertiesSet()的实现:

 

 

 

 可以发现getObject()方法中指定了 MethodInvokingFactoryBean 的实例化方式,并且为单例的,而afterPropertiesSet()方法中除了指定 initialized值为true以外,重要的是调用的prepare()方法:

在prepare()方法中通过反射获取SecurityUtils目标类和其目标方法setSecurityManager(SecurityManager  securityManager),然后执行invokeWithTargerExceptin()方法将SecurityUtils类中的SecurityManager属性进行初始化,

并且为静态变量。

 

接着我们再看 DefaultWebSecurityManager 的配置:

看到这里就知道了为什么DefaultWebSecurityManager 的配置中引入MyRealm实例了,并且在一系列构造方法中都进行了各自的变量初始化。

接下来看 ShiroFilterFactoryBean的配置:

从上图可以看出我们应该直接查看ShiroFilterFactoryBean类中的getObject()方法和 postProcessBeforeInitialization(Object bean, String beanName)方法,

 

 

 可以看出主要是通过调用createInstance()方法进行实例化的,但是在这个过程中调用了createFilterChainManager()方法,

在createFilterChainManager()方法中进行了注册Shiro一些默认的Filter以及我们自己配置的Filter,并且对我们配置的路径访问权限也进行了全部解析。

 

到这里,相信大家对Shiro的运行流程有了一个基本的认识。最后我们来看看登录的一个认证授权过程,到底是怎么调用自定义MyRealm的??带着这个问题继续往下看:

LoginController.java中login()方法

从上图可以看出Security.getSubject()获取到的是一个Subject的对象,查看getSubject()方法,得知其来源

 

可以看出创建Sucject对象的时候传入了SecurityManager对象,而这个SecurityManager对象也就是在之前在SecurityUtils中初始化好的SecurityManager,以及Builder类中的

成员变量subjectContext是通过 newSubjectContextInstance()方法进行创建并初始化的:

接着我们查看subject对象中的login()方法,会发现为什么进入的是 DelegatingSubject类中的login方法,继续查看源码:

这下就找到了进入login方法的入口:

继续查看SecurityManager类中的login()方法:

 

 

走到这里终于找到了我们自定义的MyRealm存放的位置以及进行认证的地方:

 

登录认证通过后实际上也几乎完成了Shiro的认证,然后登录授权只是赋予菜单访问权限,这个就需要结合Shiro的标签使用。

注意:整个过程的描述可能有点概况,如果还要深入了解,依此查看源码即可。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/liysoftware/p/9436440.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值