之前的项目的授权框架用的是spring-sercet,其实Shiro和它类似,都是用于用户认证,授权。所以看Shiro的源码理解起来不是很费劲,如果你之前没有阅读过有关权限框架的,或者没有接触过的话,可以先去了解下内部过程。
此篇主要分析下Shiro的加载过程。
老规矩,从web.xml入手,因为这种类似的权限框架大多是通过Filter来实行过滤的。
1. web.xml
<!-- Shiro Filter -->
<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只是一个代理,它为bean的获取提供了便利,其实很多人一开始不熟悉,以为这边就是Shrio的入口。其实不是,此处配置说明最终执行流程交给了shiroFilter。
2. spring-shiro.xml
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login.do"/>
<property name="filterChainDefinitions">
<value>
/login.do = anon
/dologin.do = anon
/** = authc <!--需要认证 -->
</value>
</property>
</bean>
可以看见ShiroFilterFactoryBean才是shiro配置里的核心类,shiroFilter这个bean就是通过它生成出来的。这个bean当前配置了几个属性。
securityManager:安全管理器
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--安全管理器-->
<property name="sessionManager" ref="sessionManager" />
<!-- 自定义数据访问连接器-->
<property name="realm" ref="shiroRealm" />
<!-- 自定义缓存设置-->
<property name="cacheManager" ref="cacheManager" />
</bean>
<!-- 设置超时时间,cookie名称等-->
<bean id="sessionManager"
class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- session 设置,一般可以继承redis存放session-->
<property name="sessionDAO" ref="sessionDAO" />
<property name="sessionIdCookie" ref="cookie"/>
<property name="sessionIdCookieEnabled" value="true"/>
</bean>
filterChainDefinitions:过滤器配置,配置一些请求的访问限制策略。
3.ShiroFilterFactoryBean
3.1 setFilterChainDefinitions
因为配置文件里有属性的注入,所以我们先去看下filterChainDefinitions的set操作。主要就是存储XML内配置的拦截策略。
public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor {
private Map<String, String> filterChainDefinitionMap;
public void setFilterChainDefinitions(String definitions) {
Ini ini = new Ini();
ini.load(definitions);
Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
if (CollectionUtils.isEmpty(section)) {
section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
}
setFilterChainDefinitionMap(section);
}
/**
* /login.do = anon
* /dologin.do = anon
* /** = authc
* 保存为map格式的数据就可以知道哪些请求应用哪些拦截策略,比如anno,authc
*
*/
public void setFilterChainDefinitionMap(Map<String, String> filterChainDefinitionMap) {
this.filterChainDefinitionMap = filterChainDefinitionMap;
}
}
3.1 getObject
另外我们看见这个类继承自FactoryBean,因此它肯定是同过getObject获取到shiroFilter这个对象,因此我们直接看这个方法。
public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor {
public Object getObject() throws Exception {
if (instance == null) {
instance = createInstance();
}
return instance;
}
}
主要方法就是createInstance,此方法内主要分成3个步骤分别组装核心数据securityManager,FilterChainManager,PathMatchingFilterChainResolver。
1.SecurityManager securityManager = getSecurityManager();
2.FilterChainManager manager = createFilterChainManager();
3.PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
protected AbstractShiroFilter createInstance() throws Exception {
log.debug("Creating Shiro Filter instance.");
// 1. 获取安全配置器
SecurityManager securityManager = getSecurityManager();
if (securityManager == null) {
String msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
}
if (!(securityManager instanceof WebSecurityManager)) {
String msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
}
// 2. 创建过滤器链管理器
FilterChainManager manager = createFilterChainManager();
// 3. 创建基于路径匹配的过滤器链解析器
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
}
createFilterChainManager();
protected FilterChainManager createFilterChainManager() {
//获取默认过滤器链管理器
DefaultFilterChainManager manager = new DefaultFilterChainManager();
Map<String, Filter> defaultFilters = manager.getFilters();
//配置一些全局的配置参数,主要设置loginUrl,successUrl等
for (Filter filter : defaultFilters.values()) {
applyGlobalPropertiesIfNecessary(filter);
}
//获取配置文件内配置的个性化Filter,不过此例中没有配置
Map<String, Filter> filters = getFilters();
if (!CollectionUtils.isEmpty(filters)) {
for (Map.Entry<String, Filter> entry : filters.entrySet()) {
String name = entry.getKey();
Filter filter = entry.getValue();
applyGlobalPropertiesIfNecessary(filter);
if (filter instanceof Nameable) {
((Nameable) filter).setName(name);
}
manager.addFilter(name, filter, false);
}
}
//构造过滤器链
Map<String, String> chains = getFilterChainDefinitionMap();
if (!CollectionUtils.isEmpty(chains)) {
for (Map.Entry<String, String> entry : chains.entrySet()) {
//此处获取到配置文件内配置的过滤策略,设置到FilterChainManager内,
// 这样就知道哪些url配置了哪些规则以及对应的Filter
// url = login.do chainDefinition = anon --> AnonymousFilter
// url = dologin.do chainDefinition = anon --> AnonymousFilter
// url = /** chainDefinition = authc --> FormAuthenticationFilter
String url = entry.getKey();
String chainDefinition = entry.getValue();
manager.createChain(url, chainDefinition);
}
}
return manager;
}
我们看第一步创建DefaultFilterChainManager()时做了哪些操作,可以看见给了shiro内配置的默认值和默认配置对应的Filter。
//默认过滤器链构造器,值取自DefaultFilter默认值中,
//可以看见,我们配置的anon,authc等都有自己的Filter
public DefaultFilterChainManager() {
this.filters = new LinkedHashMap<String, Filter>();
this.filterChains = new LinkedHashMap<String, NamedFilterList>();
addDefaultFilters(false);
}
protected void addDefaultFilters(boolean init) {
for (DefaultFilter defaultFilter : DefaultFilter.values()) {
addFilter(defaultFilter.name(), defaultFilter.newInstance(), init, false);
}
}
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);
}
最终构造出的mapper过滤器链如下图:filterChains内对应了配置的3种拦截方式和过滤方式以及对应的Filter类
最后pathMatchingFilterChainResolver这个解析器的设置也很简单,直接将上面获取到filterChains设置进去就行。
4. SpringShiroFilter
最后构造出一个SpringShiroFilter 返回出去,这样项目启动时SpringShiroFilter对象构建就完成了,接下来就是请求过程中的认证和授权步骤了。