我们在使用spring配置shiro时,有两处需要配置Filter相关的地方:
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
shiroFilter
/*
/common/**=anon
/search/**=anon
/static/superAdmin/**=roles[superadmin]
/static/admin/**=roles[admin]
/static/** = anon
其实真正发挥拦截器作用的只有Spring中配置的ShiroFilterFactoryBean,而web.xml中配置的拦截器DelegatingFilterProxy功能相当于是为Spring中的Filter做了一层包装, DelegatingFilterProxy相关的源码如下:
@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) {
//web.xml中配置的FilterName属性,当然也可以直接在web.xml中指定targetBeanName属性
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();//获取Spring容器的上下文对象(该方法调用由Spring提供的静态方法获取ApplicationContext容器对象)
if (wac != null) {
this.delegate = initDelegate(wac);//获取Spring配置文件中配置的拦截器,并作为其代理对象
}
}
}
}
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
//获取Spring容器中beanName=targetBeanName,类型为Filter的bean
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
该方法是在Filter的初始化阶段调用,功能是对delegate属性赋初值:在Spring容器中获取到bean名和filter-name属性值相同的bean,delegate声明如下:
private volatile Filter delegate;
可以看到,该属性也是一个Filter,然后看下DelegatingFilterProxy的拦截器doFilter实现:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// Lazily initialize the delegate if necessary.
//如果代理的Filter还没加载,就按上面的流程加载一次,略。
// Let the delegate perform the actual doFilter operation.
//调用代理对象方法,完成拦截
invokeDelegate(delegateToUse, request, response, filterChain);
}
invokeDelegate实现如下:
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);//调用代理对象的Filter完成拦截
}
第一步中我们看到了真正执行拦截的Bean其实是在Spring中配置的ShiroFilterFactoryBean(但是从名字可以看出来,它其实也不是最后执行拦截的,只是一个FactoryBean,用来生产我们需要的拦截器实例),ShiroFilterFactoryBean的相关结构如下:
可以看到,ShiroFilterFactoryBean继承了FactoryBean, BeanPostProcessor。这两个接口是Spring中用作拓展的接口,关于FactoryBean的实现:
FactoryBean接口的主要功能就是在getObject方法中返回一个对象,并把这个对象加入到Spring容器中,这样就可以被Spring容器中的其他Bean访问到,同时也可以访问其他的Bean
public Object getObject() throws Exception {
if (instance == null) {
instance = createInstance();//创建Bean
}
return instance;
}
//返回值是一个Filter,作为我们需要的拦截器
protected AbstractShiroFilter createInstance() throws Exception {
log.debug("Creating Shiro Filter instance.");
SecurityManager securityManager = getSecurityManager();//我们在Spring配置文件中配置的SecurityManager
if (securityManager == null) {
String msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
}
if (!(securityManager instanceof WebSecurityManager)) { //保证是Web环境下的SecurityManager
String msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
}
FilterChainManager manager = createFilterChainManager();//创建对我们配置的拦截信息进行管理的管理器
//Expose the constructed FilterChainManager by first wrapping it in a
// FilterChainResolver implementation. The AbstractShiroFilter implementations
// do not know about FilterChainManagers - only resolvers:
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();//负责匹配Url后调用符合的Filter
chainResolver.setFilterChainManager(manager);
//Now create a concrete ShiroFilter instance and apply the acquired SecurityManager and built
//FilterChainResolver. It doesn't matter that the instance is an anonymous inner class
//here - we're just using it because it is a concrete AbstractShiroFilter instance that accepts
//injection of the SecurityManager and FilterChainResolver:
return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);//负责执行拦截的Filter
}
上面代码中有个很重要的属性就是
FilterChainManager
从名字可以明白,这个对象的作用就是管理拦截器链,下面是创建它的源码:
protected FilterChainManager createFilterChainManager() {
DefaultFilterChainManager manager = new DefaultFilterChainManager();//创建,并加入了Shiro默认的几个拦截器
Map defaultFilters = manager.getFilters();
//添加一些属性
for (Filter filter : defaultFilters.values()) {
applyGlobalPropertiesIfNecessary(filter);
}
//对我们自己配置的Filter进行处理:添加到Filter列表中,并设置一些属性
Map filters = getFilters();
if (!CollectionUtils.isEmpty(filters)) {
for (Map.Entry entry : filters.entrySet()) {
String name = entry.getKey();
Filter filter = entry.getValue();
applyGlobalPropertiesIfNecessary(filter);
if (filter instanceof Nameable) {
((Nameable) filter).setName(name);
}
//'init' argument is false, since Spring-configured filters should be initialized
//in Spring (i.e. 'init-method=blah') or implement InitializingBean:
manager.addFilter(name, filter, false);
}
}
//build up the chains:
Map chains = getFilterChainDefinitionMap();
if (!CollectionUtils.isEmpty(chains)) {
for (Map.Entry entry : chains.entrySet()) {
String url = entry.getKey();
String chainDefinition = entry.getValue();
manager.createChain(url, chainDefinition);
}
}
return manager;
}
上面代码可以看到,Shiro在初始阶段就已经添加了一些Filter类:
protected void addDefaultFilters(boolean init) {
for (DefaultFilter defaultFilter : DefaultFilter.values()) {
addFilter(defaultFilter.name(), defaultFilter.newInstance(), init, false);
}
}
DefaultFilter是一个枚举类型,它的定义如下:
如下图中,我们在Shiro配置的拦截器链等号后面对应的处理Filter就是这上面的:
//如果需要,就为Filter添加我们配置文件中配置的successUrl、loginUrl、unauthorizedUrl(都是直接通过set设置)
private void applyGlobalPropertiesIfNecessary(Filter filter) {
applyLoginUrlIfNecessary(filter);
applySuccessUrlIfNecessary(filter);
applyUnauthorizedUrlIfNecessary(filter);
}
Map filters = getFilters(); //获取我们在配置文件中配置的自定义Filter列表
if (!CollectionUtils.isEmpty(filters)) {
for (Map.Entry entry : filters.entrySet()) {
String name = entry.getKey();
Filter filter = entry.getValue();
applyGlobalPropertiesIfNecessary(filter); //如上文中一样,如果需要就设置相关的全局属性
if (filter instanceof Nameable) {
((Nameable) filter).setName(name);
}
//'init' argument is false, since Spring-configured filters should be initialized
//in Spring (i.e. 'init-method=blah') or implement InitializingBean:
manager.addFilter(name, filter, false); //添加到Filte列表中
}
}
自定义的Filter例子如下:
//一个继承自Shiro AdviceFilter的拦截器
SpringShiroFilter是一个内部类,定义如下:
SpringShiroFilter的父类AbstractShiroFilter已经基本上实现了需要的功能,而SpringShiroFilter的功能主要就体现在传入的两个参数上:
securityManager:提供realm(密码比对、角色权限获取等)、sessionManager、cacheManager等
PathMatchingFilterChainResolver:实现对Url的比对,并根据结果选择合适的拦截器等
//Filter的创建就已经讲完了,因为ShiroFilterFactoryBean实现FactoryBean的原因,DelegatingFilterProxy获取到的Bean就是上面返回的SpringShiroFilter,并调用他的doFilter方法(这个方法在父类中,通过子类的覆写,最后完成拦截功能的方法是doFilterInternal方法,后面会讲到)