对于Spring 集成 Shiro,一般要求配置一个如下的Filter(web.xml)
<!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml -->
<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>
...
<!-- Make sure any request you want accessible to Shiro is filtered. /* catches all -->
<!-- requests. Usually this filter mapping is defined first (before all others) to -->
<!-- ensure that Shiro works in subsequent filters in the filter chain: -->
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- DelegatingFilterProxy 这个类是Spring针对Filter的委托代理模式的体现,它不是真正的目标Filter,而是在每次拦截到请求时,从Spring容器中查找名字为(即上述的shiroFilter)的Bean,并将请求委托于它,源码可见DelegatingFilterProxy.doFilter(…)
- 值得一提的是,如果容器内的目标Bean实现了org.springframework.beans.factory.FactoryBean接口,则表示该Bean是一个工厂类,此时并非直接返回它本身,而是调用工厂方法getObject()来生产一个Bean,并提供给调用者
让我们查阅Spring配置(applicationContext.xml),寻找名字为shiroFilter的Bean
<!--Shiro 核心过滤器/拦截器,拦截一切请求-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!-- override these for application-specific URLs if you like:
<property name="loginUrl" value="/login.jsp"/>
<property name="successUrl" value="/home.jsp"/>
<property name="unauthorizedUrl" value="/unauthorized.jsp"/> -->
<property name="loginUrl" value="/user/login"/>
<property name="successUrl" value="/home.jsp"/>
<property name="unauthorizedUrl" value="/unauthorized.jsp"/>
<!-- The 'filters' property is not necessary since any declared javax.servlet.Filter bean -->
<!-- defined will be automatically acquired and available via its beanName in chain -->
<!-- definitions, but you can perform instance overrides or name aliases here if you like: -->
<!-- <property name="filters">
<util:map>
<entry key="anAlias" value-ref="someFilter"/>
</util:map>
</property> -->
<property name="filterChainDefinitions">
<value>
# some example chain definitions:
#/admin/** = authc, roles[admin]
#/docs/** = authc, perms[document:read]
#/** = authc
# more URL-to-FilterChain definitions here
#开始配置
/** = anon
</value>
</property>
</bean>
ShiroFilterFactoryBean 实现了FactoryBean接口,是一个工厂Bean,按照之前所述,它会调用getObject()来提供真正的目标Filter ,查看源码
public Object getObject() throws Exception {
if (instance == null) {
instance = createInstance();
}
return instance;
}
protected AbstractShiroFilter createInstance() throws Exception {
//......
//初始化过滤器链
FilterChainManager manager = createFilterChainManager();
//......
return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
}
可以看到最终委托的目标Filter是私有内部类ShiroFilterFactoryBean.SpringShiroFilter
在SpringShiroFilter的构建过程中,即ShiroFilterFactoryBean.createInstance()代码中有这么一句
FilterChainManager manager = createFilterChainManager();
- 这是在初始化Shiro过滤器链,SpringShiroFilter主要充当管理者,先将真正干活的多个过滤器收集起来,并拼接成过滤器链(解析器),而后在每次请求转发时根据各种环境判断转发(转发给内部管理的shiro过滤器链,或转发给Tomcat的过滤器链),一般针对每一个请求,Shiro过滤器链至少能处理一次(实际逻辑更复杂)
- 根据上诉代码不断深入源码,可以查看到默认配置/添加的11个默认Filter 分别是
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);
private final Class<? extends Filter> filterClass;
private DefaultFilter(Class<? extends Filter> filterClass) {
this.filterClass = filterClass;
}
//......
}
- 看名字就很清楚
- anon用于非验证的请求,直接转发给Tomcat过滤器链而不受shiro过滤器链的影响
- authc用于用户认证,只能已登录有效的用户才能进入下一个Filter
- logout用于注销,一旦注销成功(清除用户信息、会话信息等),一般会重定向注销页面的url(302响应),可根据需要重写为返回json格式串
- perms用于访问控制(权限验证),只有具备权限才能进入下一个Filter
- roles用于访问控制(角色验证),只有属于某个角色才能进入下一个Filter
通过开头的Spring配置(applicationContext.xml),我们可以很轻易地改写shiro过滤器链(覆盖、添加、重命名等),以达到自己的需求(例如改写成Restful风格,实现JWT认证等)