Shiroソースコード分析--- FilterChain作成プロセス

Shiroでは、認証と権限制御の両方がフィルターを介して実装されます。アプリケーションには多くのフィルターが構成されている場合がありますが、異なるアクセス要求に渡す必要のあるフィルターは明らかに異なるため、要求を開始するときにどのフィルターが適用されますか、Shiroを使用して表示することは特に重要です。リクエストが通過するフィルターについて説明しましょう。

Shiroでは、リクエストが通過するフィルターを確認するために、org.apache.shiro.web.filter.mgt.FilterChainResolverインターフェースによって定義されています。インターフェースの定義は次のとおりです。

public interface FilterChainResolver {

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

}

インターフェースgetChainには、リクエストが通過するフィルターを判別し、これらのフィルターをFilterChainオブジェクトにカプセル化するために使用されるメソッドが1つだけあります。これFilterCahinは非常によく知っており、サーブレットを使用するときによく遭遇します。
FilterChainResolverこれは単なるインターフェースであり、Shiroはデフォルトの実装クラスを提供しますorg.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver。これはリクエストパスに従ってフィルターに一致します。
見てPathMatchingFilterChainResolver、私はソースについてお話しましょう前FilterChainManagerFilterChain一例として、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]たとえば、以下の構成は、chainNameis /rolechainDefinitionis、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);
        }
    }
}

この時点で、シロの作成FilterChainプロセスについて説明しました。エラーがあれば訂正してください。

- - - - - - - - - - - - - - - - 終わり - - - - - - - - - --------------

よりエキサイティングな記事については、パブリックアカウント「JavaEssentials」に注目してください。

おすすめ

転載: blog.51cto.com/xtayfjpk/2666381