欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

shiro源码篇 - shiro的filter,你值得拥有

程序员文章站 2023-11-24 22:18:46
前言 开心一刻 已经报废了一年多的电脑,今天特么突然开机了,吓老子一跳,只见电脑管家缓缓地出来了,本次开机一共用时一年零六个月,打败了全国0%的电脑,电脑管家已经对您的电脑失去信心,然后它把自己卸载了,只剩我在一旁发呆。 路漫漫其修远兮,吾将上下而求索! github:https://github. ......

前言

  开心一刻

    已经报废了一年多的电脑,今天特么突然开机了,吓老子一跳,只见电脑管家缓缓地出来了,本次开机一共用时一年零六个月,打败了全国0%的电脑,电脑管家已经对您的电脑失去信心,然后它把自己卸载了,只剩我在一旁发呆。

shiro源码篇 - shiro的filter,你值得拥有

  路漫漫其修远兮,吾将上下而求索!

  github:

  码云(gitee):

shirofilter的注册

  讲到了springboot的filter注册,但只是filter注册的一种方式:通过filterregistrationbean实现。而shiro filter的注册是采用另外的方式实现的,我们接着往下看

  shirofilterfactorybean实现了factorybean,我们来看下shirofilterfactorybean对factorybean的getobject方法的实现

shiro源码篇 - shiro的filter,你值得拥有

git图一

    可以看到createinstance()返回的是springshirofilter的实例;springshirofilter的类图如下

shiro源码篇 - shiro的filter,你值得拥有

  getobject方法只是创建了一个springshirofilter实例,并注册到了spring容器中,那是如何注册到servlet容器的呢?我们来跟下源码

    其实涉及到了,只是那时候没细讲,我们还是从那里开始

shiro源码篇 - shiro的filter,你值得拥有

gif图二

    servletcontextinitializerbeans.java的构造方法,里面有addservletcontextinitializerbeans(beanfactory)方法和addadaptablebeans(beanfactory),我们来好好瞅一瞅

    addservletcontextinitializerbeans(beanfactory)

shiro源码篇 - shiro的filter,你值得拥有
private void addservletcontextinitializerbeans(listablebeanfactory beanfactory) {
    for (entry<string, servletcontextinitializer> initializerbean : getorderedbeansoftype(
            beanfactory, servletcontextinitializer.class)) {
        addservletcontextinitializerbean(initializerbean.getkey(),
                initializerbean.getvalue(), beanfactory);
    }
}


private void addservletcontextinitializerbean(string beanname,
        servletcontextinitializer initializer, listablebeanfactory beanfactory) {
    if (initializer instanceof servletregistrationbean) {
        servlet source = ((servletregistrationbean<?>) initializer).getservlet();
        addservletcontextinitializerbean(servlet.class, beanname, initializer,
                beanfactory, source);
    }
    else if (initializer instanceof filterregistrationbean) {
        filter source = ((filterregistrationbean<?>) initializer).getfilter();
        addservletcontextinitializerbean(filter.class, beanname, initializer,
                beanfactory, source);
    }
    else if (initializer instanceof delegatingfilterproxyregistrationbean) {
        string source = ((delegatingfilterproxyregistrationbean) initializer)
                .gettargetbeanname();
        addservletcontextinitializerbean(filter.class, beanname, initializer,
                beanfactory, source);
    }
    else if (initializer instanceof servletlistenerregistrationbean) {
        eventlistener source = ((servletlistenerregistrationbean<?>) initializer)
                .getlistener();
        addservletcontextinitializerbean(eventlistener.class, beanname, initializer,
                beanfactory, source);
    }
    else {
        addservletcontextinitializerbean(servletcontextinitializer.class, beanname,
                initializer, beanfactory, initializer);
    }
}
view code

      会将spring bean工厂(beanfactory)中类型是servletcontextinitalizer类型的实例(包括servletregistrationbean、filterregistrationbean、delegatingfilterproxyregistrationbean、servletlistenerregistrationbean)添加进servletcontextinitializerbeans的initializers属性中。

private final multivaluemap<class<?>, servletcontextinitializer> initializers;

      servletcontextinitalizer的子类图如下所示

shiro源码篇 - shiro的filter,你值得拥有

      这也就是注册filter的方式,以registrationbean方式实现的,但是springshirofilter不是在这添加进servletcontextinitializerbeans的initializers中的哦

    addadaptablebeans(beanfactory)

shiro源码篇 - shiro的filter,你值得拥有
private void addadaptablebeans(listablebeanfactory beanfactory) {
    multipartconfigelement multipartconfig = getmultipartconfig(beanfactory);
    addasregistrationbean(beanfactory, servlet.class,
            new servletregistrationbeanadapter(multipartconfig));
    addasregistrationbean(beanfactory, filter.class,
            new filterregistrationbeanadapter());
    for (class<?> listenertype : servletlistenerregistrationbean
            .getsupportedtypes()) {
        addasregistrationbean(beanfactory, eventlistener.class,
                (class<eventlistener>) listenertype,
                new servletlistenerregistrationbeanadapter());
    }
}
view code

      会将beanfactory中的servlet、filter、listener实例封装成对应的registrationbean,然后添加到servletcontextinitializerbeans的initializers;部分细节没去跟,有兴趣的可以自行去跟下源代码。

    servletcontextinitializerbeans的sortedlist的内容最终如下

shiro源码篇 - shiro的filter,你值得拥有

    然后遍历这个sortedlist,逐个注入到servlet容器

shiro源码篇 - shiro的filter,你值得拥有

  小结

    springboot下有3种方式注册filter(servlet、listener类似),filterregistrationbean、@webfilter 和@bean,@webfilter我还没试过,另外这3种方式注册的filter的优先级是:filterregistrationbean > @webfilter > @bean(网上查阅的资料,我没试哦,使用过程中需要注意!)。

    不管是filterregistrationbean方式、@webfilter方式,还是@bean方式,只要是受spring容器管理,最终都会添加到servletcontextinitializerbeans的initializers中,都会成功注册到servlet容器。@webfilter方式和@bean方式注册的filter都会被封装成filterregistrationbean,然后添加进servletcontextinitializerbeans的initializers;3中方式最终殊途同归,都以filterregistrationbean的形式存在servletcontextinitializerbeans的initializers中。springshirofilter的注册算是@bean方式注册的,至此springshirofilter就注册到了servlet容器中了。

    servletcontextinitializerbeans的sortedlist如下:

private list<servletcontextinitializer> sortedlist;

    是一个有序的servletcontextinitializer list,这个有序针对的同类型,比如所有的filterregistrationbean有序,所有的servletregistrationbean有序,filterregistrationbean与servletregistrationbean之间有没有序是无意义的。

shiro中的filter链

  shiro的默认filter列表

    除了springshirofilter之外,shiro还有默认的11个filter;细心的朋友应该在git图一中已经发现了,在创建defaultfilterchainmanager,就把默认的11个filter添加到它的filters中

private map<string, filter> filters; //pool of filters available for creating chains

private map<string, namedfilterlist> filterchains; //key: chain name, value: chain

public defaultfilterchainmanager() {
    this.filters = new linkedhashmap<string, filter>();
    this.filterchains = new linkedhashmap<string, namedfilterlist>();
    adddefaultfilters(false);    // 将默认的11个filter添加到filters
}

    这11个filter具体如下

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

  filter链

    springshirofilter注册到servlet容器中,请求肯定会经过springshirofilter的dofilter方法,我们就从此开始跟一跟源代码

shiro源码篇 - shiro的filter,你值得拥有

gif图三

    上图中可能展示的不够细,主要就是两点:1、路径匹配:pathmatches(pathpattern, requesturi),默认的fliter逐个与请求uri进行匹配;2、代理filterchain:proxiedfilterchain。如果匹配不上,那么直接走servlet的filterchain,否则先走shiro的代理filterchain(proxiedfilterchain),之后再走servlet的filterchain。

    proxiedfilterchain源代码如下

shiro源码篇 - shiro的filter,你值得拥有
/*
 * licensed to the apache software foundation (asf) under one
 * or more contributor license agreements.  see the notice file
 * distributed with this work for additional information
 * regarding copyright ownership.  the asf licenses this file
 * to you under the apache license, version 2.0 (the
 * "license"); you may not use this file except in compliance
 * with the license.  you may obtain a copy of the license at
 *
 *     http://www.apache.org/licenses/license-2.0
 *
 * unless required by applicable law or agreed to in writing,
 * software distributed under the license is distributed on an
 * "as is" basis, without warranties or conditions of any
 * kind, either express or implied.  see the license for the
 * specific language governing permissions and limitations
 * under the license.
 */
package org.apache.shiro.web.servlet;

import org.slf4j.logger;
import org.slf4j.loggerfactory;

import javax.servlet.*;
import java.io.ioexception;
import java.util.list;

/**
 * a proxied filter chain is a {@link filterchain} instance that proxies an original {@link filterchain} as well
 * as a {@link list list} of other {@link filter filter}s that might need to execute prior to the final wrapped
 * original chain.  it allows a list of filters to execute before continuing the original (proxied)
 * {@code filterchain} instance.
 *
 * @since 0.9
 */
public class proxiedfilterchain implements filterchain {

    //todo - complete javadoc

    private static final logger log = loggerfactory.getlogger(proxiedfilterchain.class);

    private filterchain orig;                    // 原filterchain,也就是servlet容器的filterchain
    private list<filter> filters;                // shiro默认的11个filter
    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 {
        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);                        // 当shiro的11个filter走完之后,继续走servlet容器的filterchain
        } else {
            if (log.istraceenabled()) {
                log.trace("invoking wrapped filter at index [" + this.index + "]");
            }
            this.filters.get(this.index++).dofilter(request, response, this);    // 递归逐个走shiro的11个filter
        }
    }
}
view code

    shiro对servlet容器的filterchain进行了代理,即shirofilter在继续servlet容器的filter链的执行之前,通过proxiedfilterchain对servlet容器的filterchain进行了代理;即先走shiro自己的filter体系,然后才会委托给servlet容器的filterchain进行servlet容器级别的filter链执行;shiro的proxiedfilterchain执行流程:1、先执行shiro自己的filter链;2、再执行servlet容器的filter链(即原始的 filter)。

总结

  1、springshirofilter注册到spring容器,会被包装成filterregistrationbean,通过filterregistrationbean注册到servlet容器;

  2、一般而言,shiro的pathmatchingfilterchainresolver会匹配所有的请求,shiro对servlet容器的filterchain进行了代理,生成代理filterchain:proxiedfilterchain,请求先走shiro自己的filter链,再走servelt容器的filter链;

  3、题外话,springboot注册filter、servlet、listener方式类似,都有3种,具体是哪三种,大家去上文看;关于shiro的filter,本文没做更详细的讲解,需要了解的可以去看《跟我学shiro》

参考

  《跟我学shiro》

  shiro源码