- 注:以下分析是整合了spring框架之后的整個工作流程分析。
前言 Shiro簡介
這是shiro官方給出的功能模塊詳細的介紹可以跳轉(zhuǎn)到張開濤老師的博客:我是傳送門
本人打算從整個shiro工作的流程出發(fā),對其每一步的工作進行追蹤玻驻,因此側(cè)重于源碼的分析,對于具體如何使用识腿,大家也可以參考張開濤老師的博客牲阁。
由于本人剛參加工作,經(jīng)驗還不夠夭禽,如有錯誤的地方,望各位不吝賜教谊路。
一讹躯、Shiro初始化
在未整合spring之前,shiro在應用到web應用時,是通過對訪問路徑的過濾來進行權(quán)限控制的潮梯,也就會再web.xml中直接定義過濾器即可(mapping配置省略)骗灶,如下:
<filter>
<filter-name>ShiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
shiro的初始化工作是在web.xml中設置監(jiān)聽器完成的,配置如下:
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
而在整合spring之后秉馏,shiro為了將整個生命周期托管給spring耙旦,當然包括初始化的工作,有點不同的是過濾器采用了Spring的代理過濾器萝究。
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<!--允許代理注入免都,方便spring容器管理filter的生命周期,詳細情況帆竹,大家自行百度-->
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
我們就從這里開始跟蹤一下spring的代理過濾器是怎么個流程绕娘。
1、代理過濾器初始化
配置是按照官網(wǎng)的配置來進行的栽连。這里的啟動調(diào)用的是這個代理類的無參的構(gòu)造方法险领。
接下來是對真實的過濾器進行初始化:調(diào)用initFilterBean()方法。
我們來看一下這個方法的代碼秒紧。
@Override
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// 如果目標bean的名字沒有指定绢陌,那么就使用過濾器的名字,
// 也就是前面配置:<filter-name>shiroFilter</filter-name>(注意是shiroFilter)
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// 這里就可以根據(jù)目標bean的名字熔恢,從spring工廠中獲取相應的實例了下面。
// 至于具體如何獲取實例,稍后會進行說明
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
this.delegate = initDelegate(wac);
}
}
}
}
- 注意:spring的代理過濾器有一個懶加載的過程绩聘,因此在執(zhí)行doFilter()的時候會判斷一次是否有初始化Filter類沥割。具體代碼如下(應該很容易看懂的,就不詳細說明了):
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 懶加載
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
}
this.delegate = initDelegate(wac);
}
delegateToUse = this.delegate;
}
}
// 執(zhí)行真正的過濾器的doFilter()方法
invokeDelegate(delegateToUse, request, response, filterChain);
}
2凿菩、shiroFilter實例的創(chuàng)建
shiroFilter在集成spring之后的創(chuàng)建是由:java org.apache.shiro.spring.web.ShiroFilterFactoryBean
這一工廠類進行實例化的机杜。
2.1、工廠類的初始化配置
這一工廠類的初始化是由spring進行的衅谷,我們來看看配置即可椒拗。
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!--shiro的核心就是這個manager,這里是說明過濾器的初始化工作获黔,就暫不進行詳細分析-->
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/index/login"/>
<property name="filterChainDefinitions">
<value>
/static/**=anon
/logout=logout
</value>
</property>
</bean>
- 注意這里的id要與web.xml中代理過濾器的filter-name保持一致
至此蚀苛,shiro的初始化配置工作算是基本結(jié)束了。但是想必有些人會覺得迷茫玷氏,你明明使用的java org.apache.shiro.spring.web.ShiroFilterFactoryBean
堵未,這是一個工廠類,而我們需要的應該是一個javax.servlet.Filter
類才對盏触。一開始我也很迷糊渗蟹,不知道咋地块饺,在突然想起一個鬼東西,spring的什么鬼工廠雌芽,然后就去找spring的源碼了授艰。然后就找到這么一個東西,現(xiàn)在貼出來給大家看看(相信大家一看就能懂了)世落,順便也復習一下spring淮腾。
這個類是:java org.springframework.beans.factory.support.StaticListableBeanFactory
graph BT
org.springframework.beans.factory.support.StaticListableBeanFactory-->org.springframework.beans.factory.ListableBeanFactory
org.springframework.beans.factory.ListableBeanFactory-->org.springframework.beans.factory.BeanFactory
@Override
public Object getBean(String name) throws BeansException {
String beanName = BeanFactoryUtils.transformedBeanName(name);
Object bean = this.beans.get(beanName);
if (bean == null) {
throw new NoSuchBeanDefinitionException(beanName,
"Defined beans are [" + StringUtils.collectionToCommaDelimitedString(this.beans.keySet()) + "]");
}
// Don't let calling code try to dereference the
// bean factory if the bean isn't a factory
if (BeanFactoryUtils.isFactoryDereference(name) && !(bean instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, bean.getClass());
}
if (bean instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
try {
// 大家注意這里
return ((FactoryBean<?>) bean).getObject();
}
catch (Exception ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
}
else {
return bean;
}
}
- 這篇其實還沒有涉及到shiro的源碼,都還是spring的知識屉佳。在完成了初始化工作之后谷朝,我們來看看shiro是怎樣進行驗證的,下圖是shiro官方提供的驗證步驟忘古。(更新中)
簡化驗證流程圖。
細化驗證流程圖(好像不是那么詳細哈哈诅诱,看有沒有時間髓堪,自己做一張更詳細的)
- 所有圖片均來自shiro官方:我是傳送門