项目中用了shiro很久了,但对于其执行原理一直没研究过。后来在项目中做防盗链功能时候,因为不能拦截被shiro认证的白名单接口,不得不研究了shiro源码。
1、shiro首先是一个过滤器,filter基本功能肯定有。
我们知道filter最重要的一个接口是 void doFilter(ServletRequest request, ServletResponse response,FilterChain chain)。
所以我们由这里开始.
abstract class OncePerRequestFilter extends NameableFilter {
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain){
//略
doFilterInternal(request, response, filterChain);
}
}
这个抽象类里面有个final的doFilter()函数,因为是final,所以其真正的实现放在doFilterInternal。
2、doFilterInternal函数
abstract class AbstractShiroFilter extends OncePerRequestFilter{
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain){
final Subject subject = createSubject(request, response);
subject.execute(new Callable() {
public Object call() throws Exception {
updateSessionLastAccessTime(request, response);
executeChain(request, response, chain);
return null;
}
});
}
这里我们主要看 executeChain(request, response, chain);
3. executeChain函数
protected void executeChain(ServletRequest req,ServletResponse resp,FilterChain origChain) {
FilterChain chain = getExecutionChain(reqt, res, origChain);
chain.doFilter(req, res);
}
只有两个步骤,首先是获取shiro自定义的过滤链对象,然后执行shiro自定义的过滤。这里只讨论getExecutionChain函数
4.getExecutionChain函数
protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain){
//略
FilterChainResolver resolver =getFilterChainResolver();
FilterChain chain = resolver.getChain(request, response, origChain);
return chain;
}
这个函数是首先获取一个过滤链解析器,然后通过解析器再去获取过滤链对象 。这里要说下,获取的解析器resolver
其实在项目启动时就被实例好了,作为shiro对象的一个属性。这个属性也是定义在AbstractShiroFilter类中
private FilterChainResolver filterChainResolver;
至于怎么初始化这个对象,这里就不讨论了,有兴趣的可以研究下shiro的实例化过程,它是被工厂生产出来的。
继续回归之前的话题,看看 resolver.getChain(request, response, origChain)函数
5. resolver.getChain(request, response, origChain)函数
public class PathMatchingFilterChainResolver implements FilterChainResolver{
public FilterChain getChain(request,response,originalChain){
FilterChainManager filterChainManager = getFilterChainManager();
String requestURI = getPathWithinApplication(request);
for (String pathPattern : filterChainManager.getChainNames()) {
if (pathMatches(pathPattern, requestURI)) {
return filterChainManager.proxy(originalChain, pathPattern);
}
}
}
public FilterChainManager getFilterChainManager() {
return filterChainManager;
}
private FilterChainManager filterChainManager
}
filterChainManager对象也会在项目启动时进行实例化。这里就不讨论了。
我们看看 filterChainManager.getChainNames() 函数,记得shiro配置时的 filterChainDefinitions吗?
<property name="filterChainDefinitions">
<value>
/statics/**=anon
/api/**=anon
</value>
</property>
filterChainManager.getChainNames() 获取出来的就是配置的shiro过滤路径。
这样,如果ruquest的url能够匹配 filterChainManager.getChainNames() 中的过滤路径,就返回对应chain。
6. filterChainManager.proxy(originalChain, pathPattern)函数
这个其实没什么好说的,就是根据相应的url生成一个filterChain对象。
重点看看生成的filterChain对象有什么属性,FilterChain是个接口,其最终的实现类是ProxiedFilterChain
看看ProxiedFilterChain 类的属性和方法
public class ProxiedFilterChain implements FilterChain {
private FilterChain orig;
private List<Filter> filters;
private int index = 0;
public void doFilter(ServletRequest request, ServletResponse response) {
if (this.filters == null || this.filters.size() == this.index) {
//we've reached the end of the wrapped chain, so invoke the original one:
if (log.isTraceEnabled()) {
log.trace("Invoking original filter chain.");
}
this.orig.doFilter(request, response);
} else {
if (log.isTraceEnabled()) {
log.trace("Invoking wrapped filter at index [" + this.index + "]");
}
this.filters.get(this.index++).doFilter(request, response, this);
}
}
}
可以看到有个filter列表属性。首先项目启动时,会缓存shiro内置的全部Filter对象。
这样每一个请求来到,shiro都能保证请求能找到对应的Filter对象,然后不同的Filter对象都有自己的doFilter逻辑。
而且可以看到当filters存在多个时,会先循环执行完,然后再交给下一个过滤器。
所以,我们在web.xml配置过滤器时,应该把shiro过滤器放在最前面。