从Shiro配置中可以看出,Shiro的起始类是DelegatingFilterProxy,DelegatingFilterProxy委托给Shiro自己的一系列Filter做过滤处理,这是后话;
SpringMvc中web.xml配置:
<!-- shiroFilter -->
<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>
SpringBoot中的配置代码:
@Bean
public FilterRegistrationBean authorizeFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
DelegatingFilterProxy proxy = new DelegatingFilterProxy();
proxy.setTargetFilterLifecycle(true);
proxy.setTargetBeanName("shiroFilter");
filterRegistrationBean.setName("authorizeFilter");
filterRegistrationBean.setFilter(proxy);
filterRegistrationBean.setUrlPatterns(Lists.newArrayList("/*"));
filterRegistrationBean.setOrder(2);
return filterRegistrationBean;
}
关键在于这行代码,设置目标类的名称,也就是真正处理业务的Java对象:
proxy.setTargetBeanName("shiroFilter");
由此可见,DelegatingFilterProxy代理的是shiroFilter这个类,那么这个类在哪里呢?(下面是SpringBoot中的代码,以下的所有代码均为SpringBoot中的代码)
@Bean
public CustomShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager,
CustomFilter customFilter,
AnyRolesAuthorizationFilter anyRolesAuthorizationFilter,
AnyPermissionsAuthorizationFilter anyPermissionsAuthorizationFilter) {
CustomShiroFilterFactoryBean shiroFilter = new CustomShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
shiroFilter.setLoginUrl("/admin/index/unAuthenticated");
shiroFilter.setUnauthorizedUrl("/admin/index/unAuthorized");
Map<String, Filter> filters = new HashMap<>();
filters.put("c", customFilter);
filters.put("roles", anyRolesAuthorizationFilter);
filters.put("perms", anyPermissionsAuthorizationFilter);
shiroFilter.setFilters(filters);
String shiroChains = "";
try {
shiroChains = IOUtils.toString(ShiroConfig.class.getClassLoader().getResourceAsStream("shiro-chains.txt"), "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
shiroFilter.setFilterChainDefinitions(shiroChains);
return shiroFilter;
}
这段代码想必都不陌生,这里不做赘述,现在可以回到DelegatingFilterProxy中了。
DelegatingFilterProxy继承自GenericFilterBean,GenericFilterBean实现了Filter中的init(FilterConfig),并且提供了一个无参的initFilterBean()供子类实现:
DelegatingFilterProxy # initFilterBean 初始化过滤器实例:
@Override
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// If no target bean name specified, use filter name.
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// Fetch Spring root application context and initialize the delegate early,
// if possible. If the root application context will be started after this
// filter proxy, we'll have to resort to lazy initialization.
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
this.delegate = initDelegate(wac);
}
}
}
}
DelegatingFilterProxy # initDelegate
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
从wac(WebApplicationContext)中取getTargetBeanName这个Bean,还记得这行代码吗?
proxy.setTargetBeanName("shiroFilter");
所以现在取的就是“shiroFilter”命名的Bean,也就是文章刚开始由@Bean注解的CustomShiroFilterFactoryBean这个类,这个类是我自定义的一个类,继承自ShiroFilterFactoryBean:
public class CustomShiroFilterFactoryBean extends ShiroFilterFactoryBean {
}
ShiroFactoryBean实现了FactoryBean,在Spring中有两种Bean的实现方式,一种就是普通的Java类,另一种就是实现了FactoryBean的Java类,实现了FactoryBean的Java类在取它的实例时,并不是取它自身,而是调用它的getObject来取具体的实例;
public interface FactoryBean<T> {
T getObject() throws Exception;
}
ShiroFactoryBean是如何实现该方法的呢?咱们接着走:
ShiroFilterFactoryBean # getObject
public Object getObject() throws Exception {
if (this.instance == null) {
this.instance = this.createInstance();
}
return this.instance;
}
初始化的时候,this.instance = null;所以执行:
ShiroFilterFacotyBean # createInstance
protected AbstractShiroFilter createInstance() throws Exception {
log.debug("Creating Shiro Filter instance.");
SecurityManager securityManager = this.getSecurityManager();
String msg;
if (securityManager == null) {
msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
} else if (!(securityManager instanceof WebSecurityManager)) {
msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
} else {
FilterChainManager manager = this.createFilterChainManager();
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
return new ShiroFilterFactoryBean.SpringShiroFilter((WebSecurityManager)securityManager, chainResolver);
}
}
其中主要有3个步骤:
1.取安全管理器 SecurityManager securityManager = this.getSecurityManager();(在配置时注入)
2.创建过滤器链管理器 FilterChainManager manager = this.createFilterChainManager();
3.创建返回Shiro过滤器 new ShiroFilterFactoryBean.SpringShiroFilter(...);
至此,初始化过程就完成了。
其中第2个步骤最为重要,是自定义加载方式最好的时机:
ShiroFilterFactoryBean # createFilterChainManager
protected FilterChainManager createFilterChainManager() {
DefaultFilterChainManager manager = new DefaultFilterChainManager();
Map<String, Filter> defaultFilters = manager.getFilters();
Iterator var3 = defaultFilters.values().iterator();
while(var3.hasNext()) {
Filter filter = (Filter)var3.next();
this.applyGlobalPropertiesIfNecessary(filter);
}
Map<String, Filter> filters = this.getFilters();
String name;
Filter filter;
if (!CollectionUtils.isEmpty(filters)) {
for(Iterator var10 = filters.entrySet().iterator(); var10.hasNext(); manager.addFilter(name, filter, false)) {
Entry<String, Filter> entry = (Entry)var10.next();
name = (String)entry.getKey();
filter = (Filter)entry.getValue();
this.applyGlobalPropertiesIfNecessary(filter);
if (filter instanceof Nameable) {
((Nameable)filter).setName(name);
}
}
}
Map<String, String> chains = this.getFilterChainDefinitionMap();
if (!CollectionUtils.isEmpty(chains)) {
Iterator var12 = chains.entrySet().iterator();
while(var12.hasNext()) {
Entry<String, String> entry = (Entry)var12.next();
String url = (String)entry.getKey();
String chainDefinition = (String)entry.getValue();
manager.createChain(url, chainDefinition);
}
}
return manager;
}
取默认的过滤器链配置,其中包含最基本的过滤器,比如 anon、roles、perms、authc等;
DefaultFilterChainManager manager = new DefaultFilterChainManager();
public enum DefaultFilter {
anon(AnonymousFilter.class),
authc(FormAuthenticationFilter.class),
authcBasic(BasicHttpAuthenticationFilter.class),
logout(LogoutFilter.class),
noSessionCreation(NoSessionCreationFilter.class),
perms(PermissionsAuthorizationFilter.class),
port(PortFilter.class),
rest(HttpMethodPermissionFilter.class),
roles(RolesAuthorizationFilter.class),
ssl(SslFilter.class),
user(UserFilter.class);
}
创建过滤器链,将自定义的拦截放入过滤器链中,其中filterChains非常重要,在动态配置拦截的时候将会用到:
manager.createChain(url, chainDefinition);
- DefaultFilterChainManager # createChain 创建chain,参数依次是要拦截的url以及权限控制;
- DefaultFilterChainManager # addToChain 一些参数校验;
- DefaultFilterChainManager # ensureChain 对filterChains做验证,保证有此chain;
- 最后将chain中添加此filter;
现在,初始化过程就结束了。
其中需要知道的是,DelegatingFilterProxy是属于web级别的过滤器,而SpringMvc中的HandlerInterceptor是在DispatcherServlet分发请求时做的拦截,也就是说被Shiro委托的一系列Filter都在请求到达DispatcherServlet前执行,只有知道了整个系统的初始化过程、执行过程,我们才能设计出更优秀的系统,即便出现问题时也能快速定位。
Shiro拦截请求以及做身份、权限验证的过程,将在下一篇文章中讲述。
谢谢。
公众号搜索:以镒称铢