Shiro source code analysis---FilterChain creation process

In Shiro, both authentication and permission control are implemented through filters. There may be many filters configured in the application, but the filters that need to pass for different access requests are definitely different, so when When initiating a request, which filters will be applied, it is particularly important for us to use Shiro to display; let's talk about which filters a request will go through.

In Shiro, to confirm which filters a request will go through is org.apache.shiro.web.filter.mgt.FilterChainResolverdefined by the interface, the following is the interface definition:

public interface FilterChainResolver {

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

}

There is only one method in the interface getChain, which is used to determine which filters the request passes through, and then encapsulate these filters into an FilterChainobject. FilterCahinWe are very familiar with it, and we often meet when using Servlets.
FilterChainResolverIt is just an interface, Shiro provides a default implementation class org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver, which will match the filter according to the request path.
Look PathMatchingFilterChainResolverbefore Let me talk about the source FilterChainManagerof FilterChainis how to come to ini configuration as an example:

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

Where /static/**, /formfilterlogin, /roleit is under FilterChainManagerthe management of FilterChainnames. Let's see FilterChainManagerhow it is managed FilterChain.
Shiro provides FilterChainManagera default implementation:, org.apache.shiro.web.filter.mgt.DefaultFilterChainManagerits createChainmethod will be org.apache.shiro.web.config.IniFilterChainResolverFactorycalled when the system starts to create each FilterChain. Below /role=authc,roles[admin]configuration, for example, chainNameis /role, chainDefinitionis,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);
}

After understanding FilterChainManagerhow to create and store FilterChain, let's take a look at FilterChainResolverhow to determine which filters a request needs to go through.

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;
}

The following is the DefualtFilterChainManager.proxymethod source code:

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);
}

NamedFilterListThe implementation class isorg.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);
        }
    }
}

At this point, the Shiro creation FilterChainprocess has been explained. Please correct me if there are any errors.

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

For more exciting articles in time, please pay attention to the public account "Java Essentials".

Guess you like

Origin blog.51cto.com/xtayfjpk/2666381
Recommended