您的位置:首页 > 大数据 > 人工智能

Shiro源码分析---FilterChain创建过程

2021-03-20 11:02 826 查看

在Shiro中,无论是认证还是权限控制都是通过过滤器来实现的,在应用中可能会配置很多个过滤器,但对于不同的访问请求所需要经过的过滤器肯定是不一样的,那么当发起一个请求时,到底会应用上哪些过滤器,对于我们使用Shiro就显示得格外重要;下面就来讲讲一个请求到底会经过哪些过滤器。

在Shiro中,确证一个请求会经过哪些过滤器是通过

org.apache.shiro.web.filter.mgt.FilterChainResolver
接口来定义的,下面是接口定义:

public interface FilterChainResolver {

FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain);

}

接口中只有一个方法

getChain
,就是用于确定请求到底要经过哪些过滤器,然后将这些过滤器封装成一个
FilterChain
对象,
FilterCahin
我们很熟悉,在使用Servlet的时候经常见面。
FilterChainResolver
只是一个接口,Shiro提供了一个默认的实现类
org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver
,该实现类会根据请求路径进行匹配过滤器。
在看
PathMatchingFilterChainResolver
源码之前先说一下
FilterChainManager
中的
FilterChain
是怎么来的,以ini配置为例:

[urls]
/static/**=anon
/formfilterlogin=authc
/role=authc,roles[admin]

其中

/static/**
/formfilterlogin
/role
就是受
FilterChainManager
管理的
FilterChain
的名称。下面看看
FilterChainManager
是如何管理
FilterChain
的。
Shiro提供了
FilterChainManager
一个的默认实现:
org.apache.shiro.web.filter.mgt.DefaultFilterChainManager
,其
createChain
方法会在系统启动的时候被
org.apache.shiro.web.config.IniFilterChainResolverFactory
调用,用于创建各个
FilterChain
。下面以
/role=authc,roles[admin]
配置为例,
chainName
就是
/role
chainDefinition
就是
authc,roles[admin]

public void createChain(String chainName, String chainDefinition) {
if (!StringUtils.hasText(chainName)) {
throw new NullPointerException("chainName cannot be null or empty.");
}
if (!StringUtils.hasText(chainDefinition)) {
throw new NullPointerException("chainDefinition cannot be null or empty.");
}

if (log.isDebugEnabled()) {
log.debug("Creating chain [" + chainName + "] from String definition [" + chainDefinition + "]");
}

// filterTokens数组有两个元素,第一个为authc,第二个为roles[admin],因为配置时可以配置多个Filter,
// 多个Filter间以逗号分隔
String[] filterTokens = splitChainDefinition(chainDefinition);

for (String token : filterTokens) {
// 对roles[admin]进行分隔,数组中第一个元素为roles,第二个为admin
String[] nameConfigPair = toNameConfigPair(token);

// 添加FilterChain
addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);
}
}
public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {
if (!StringUtils.hasText(chainName)) {
throw new IllegalArgumentException("chainName cannot be null or empty.");
}

// 根据filterName(role)查找出Filter
Filter filter = getFilter(filterName);
if (filter == null) {
throw new IllegalArgumentException("There is no filter with name '" + filterName +
"' to apply to chain [" + chainName + "] in the pool of available Filters.  Ensure a " +
"filter with that name/path has first been registered with the addFilter method(s).");
}
// 应用FilterChain配置,以roles[amdin]为例,调用该方法后roles过滤器就知道其进行拦截器需要admin角色
applyChainConfig(chainName, filter, chainSpecificFilterConfig);

// 如果chainName以前没处理过则创建一个新的NamedFilterList对象,如果处理过则返回以前的NamedFilterList对象
// 所以在FilterChainManager中,存储Filter的是NamedFilterList对象
NamedFilterList chain = ensureChain(chainName);
// 将过滤器添加至链中
chain.add(filter);
}

在了解了

FilterChainManager
是如何创建与存储
FilterChain
以后,再来看看
FilterChainResolver
是如何确定一个请求需要经过哪些过滤器的。

public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
FilterChainManager filterChainManager = getFilterChainManager();
// 判断FilterChainManager中是否有FilterChain,如果没有则返回null
if (!filterChainManager.hasChains()) {
return null;
}
// 获取请求URI
String requestURI = getPathWithinApplication(request);

// FilterChain的名称就是路径匹配符,如果请求URI匹配上了某个FilterChain
// 则调用FilterChainManager.proxy方法返回一个FilterChain对象,注意是返回第一个匹配FilterChain
// 也就是说如果在ini配置文件中配置了多个同名的FilterChain,则只有第一个FilterChain有效
for (String pathPattern : filterChainManager.getChainNames()) {
if (pathMatches(pathPattern, requestURI)) {
if (log.isTraceEnabled()) {
log.trace("Matched path pattern [" + pathPattern + "] for requestURI [" + requestURI + "].  " +
"Utilizing corresponding filter chain...");
}
return filterChainManager.proxy(originalChain, pathPattern);
}
}

return null;
}

下面是

DefualtFilterChainManager.proxy
方法源码:

public FilterChain proxy(FilterChain original, String chainName) {
// 路径模式匹配(如/static/**)就是FilterChain名称
// 根据FilterChain名称查找NamedFilterList对象(存储了配置的Filter)
NamedFilterList configured = getChain(chainName);
if (configured == null) {
String msg = "There is no configured chain under the name/key [" + chainName + "].";
throw new IllegalArgumentException(msg);
}
// 调用NamedFilterList.proxy方法
return configured.proxy(original);
}

NamedFilterList
的实现类为
org.apache.shiro.web.filter.mgt.SimpleNamedFilterList

public FilterChain proxy(FilterChain orig) {
// 返回ProxiedFilterChain对象,该对象就是当一个请求到来后需要被执行的FilterChain对象
// 该对象只是一个代理对象,代理了两个FilterChain,一个是NamedFilterList,另一个是原始的FilterChain对象
// 原始的FilterChain对象包含了在web.xml中配置并应用上的Filter
return new ProxiedFilterChain(orig, this);
}
public class ProxiedFilterChain implements FilterChain {

private static final Logger log = LoggerFactory.getLogger(ProxiedFilterChain.class);

private FilterChain orig;
private List<Filter> filters;
private int index = 0;

public ProxiedFilterChain(FilterChain orig, List<Filter> filters) {
if (orig == null) {
throw new NullPointerException("original FilterChain cannot be null.");
}
this.orig = orig;
this.filters = filters;
this.index = 0;
}

public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
// 可以看出,先执行原始Filter,再执行NamedFilterList中的Filter
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);
}
}
}

至此,Shiro创建

FilterChain
过程讲解完毕,如有错误之处,尽请指正。

-------------------------------- END -------------------------------

及时获取更多精彩文章,请关注公众号《Java精讲》。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: