SpringBoot整合shiro系列-SpingBoot是如何将shiroFilter注册到servlet容器中的

Posted 剑气吾霜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot整合shiro系列-SpingBoot是如何将shiroFilter注册到servlet容器中的相关的知识,希望对你有一定的参考价值。

一、先从配置类入手,主要是@Bean了一个ShiroFilterFactoryBean:

@Data
@Configuration
@Slf4j
@EnableConfigurationProperties(ShiroProperties.class)
public class ShiroConfiguration {

    //.......这里省略了大部分跟主题无关的配置代码

    

   
    /**
     * 这里名字叫 shiroFilter,后续通过FilterRegistrationBean从spring容器中向servlet容器中注册filter
     * @param securityManager
     * @return
     * @author 
     * @create 
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean (DefaultWebSecurityManager securityManager,
                                                             ShiroProperties sp,IUserService userService){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //配置shiroFilter子过滤器
        loadFilter(shiroFilterFactoryBean,getCasFilter(sp,userService),getJwtFilter(),getWeComFilter());
        //配置拦截规则
        loadShiroFilterChain(shiroFilterFactoryBean, sp);
        return shiroFilterFactoryBean;
    }

   /**
     * CAS过滤器
     */
    @Bean
    public CustomizedCasFilter getCasFilter(ShiroProperties sp, IUserService userService) {
        CustomizedCasFilter casFilter = new CustomizedCasFilter();
        //.......
        return casFilter;
    }

    /**
     * JWT过滤器
     *
     * @return
     * @author 
     * @create 2020年8月19日
     */
    @Bean
    public CustomizedJwtFilter getJwtFilter() {
        CustomizedJwtFilter jwtFilter = new CustomizedJwtFilter();
        return jwtFilter;
    }

    /**
     *  企业微信过滤器
     * @param
     * @return
     */
    @Bean
    public CustomizedWeComFilter getWeComFilter() {
        CustomizedWeComFilter weComFilter = new CustomizedWeComFilter();
        return weComFilter;
    }

    /**
     * 添加过滤器
     * @param bean
     * @param casFilter
     * @param jwtFilter
     */
    private void loadFilter(ShiroFilterFactoryBean bean,CasFilter casFilter,
                            CustomizedJwtFilter jwtFilter,
                            CustomizedWeComFilter weComFilter) {
        Map<String, Filter> filters = new HashMap<>();
        filters.put("cas", casFilter);
        filters.put("jwt", jwtFilter);
        filters.put("weCom",weComFilter);
        bean.setFilters(filters);
    }

    /**
     * 加载shiroFilter权限控制规则
     *
     * @author
     * @create
     */
    private void loadShiroFilterChain (ShiroFilterFactoryBean shiroFilterFactoryBean, ShiroProperties sp){
        //下面这些规则配置最好配置到配置文件中
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // shiro集成cas后,首先添加该规则
        filterChainDefinitionMap.put("/api/**", "jwt");
        filterChainDefinitionMap.put("/cas/**", "cas");
        filterChainDefinitionMap.put("/wecom/**", "weCom");
        filterChainDefinitionMap.put("/test/**", "anon");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    }


    /**
     * 在spring容器处在某一声明周期时时,为shiro中的相关bean执行对应的bean生命周期方法
     *
     * @author
     * @create
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor () {
        return new LifecycleBeanPostProcessor();
    }

    .......
}

 

 

二、ShiroFilterFactoryBean(源码解析,代码中注释带序号的为代码执行顺序)-- 向Spring容器中注入了SpringShiroFilter:

public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor {
    private static final transient Logger log = LoggerFactory.getLogger(ShiroFilterFactoryBean.class);
    private SecurityManager securityManager;
    //这里是我们向注入的多个filter 见配置类中的loadFilter()方法
    private Map<String, Filter> filters = new LinkedHashMap();
    private List<String> globalFilters = new ArrayList();
    //见配置类中loadShiroFilterChain 
    private Map<String, String> filterChainDefinitionMap;
    private String loginUrl;
    private String successUrl;
    private String unauthorizedUrl;
    private AbstractShiroFilter instance;

    public ShiroFilterFactoryBean() {
        this.globalFilters.add(DefaultFilter.invalidRequest.name());
        this.filterChainDefinitionMap = new LinkedHashMap();
    }

    public SecurityManager getSecurityManager() {
        return this.securityManager;
    }

    ......

    public Map<String, Filter> getFilters() {
        return this.filters;
    }

    public Map<String, String> getFilterChainDefinitionMap() {
        return this.filterChainDefinitionMap;
    }

    ......

    // 1、  这里会在spring容器加载的时候调用,向spring容器中注入getObject()返回的对象
    public Object getObject() throws Exception {
        if (this.instance == null) {
            //2、调用createInstance()方法
            this.instance = this.createInstance();
        }

        return this.instance;
    }

    public Class getObjectType() {
        return ShiroFilterFactoryBean.SpringShiroFilter.class;
    }

    public boolean isSingleton() {
        return true;
    }

    protected FilterChainManager createFilterChainManager() {
        DefaultFilterChainManager manager = new DefaultFilterChainManager();
     
        Map<String, Filter> defaultFilters = manager.getFilters();
        Iterator var3 = defaultFilters.values().iterator();

        while(var3.hasNext()) {
            Filter filter = (Filter)var3.next();
            this.applyGlobalPropertiesIfNecessary(filter);
        }

     //4、在这个地方获取了shiroFilter中的子过滤器,即我们在配置类中设置的loadFilter()
        Map<String, Filter> filters = this.getFilters();
        String name;
        Filter filter;
        if (!CollectionUtils.isEmpty(filters)) {

        //5、这个地方迭代,并将子过滤器添加到了manager中
            for(Iterator var10 = filters.entrySet().iterator(); var10.hasNext(); manager.addFilter(name, filter, false)) {
                Entry<String, Filter> entry = (Entry)var10.next();
                name = (String)entry.getKey();
                filter = (Filter)entry.getValue();
                this.applyGlobalPropertiesIfNecessary(filter);
                if (filter instanceof Nameable) {
                    ((Nameable)filter).setName(name);
                }
            }
        }

        manager.setGlobalFilters(this.globalFilters);
        Map<String, String> chains = this.getFilterChainDefinitionMap();
        if (!CollectionUtils.isEmpty(chains)) {
            Iterator var12 = chains.entrySet().iterator();

            while(var12.hasNext()) {
                Entry<String, String> entry = (Entry)var12.next();
                String url = (String)entry.getKey();
                String chainDefinition = (String)entry.getValue();
          //这里很重要 后面DefaultFilterChainManager filterChains属性在这里添加值
                manager.createChain(url, chainDefinition);
            }
        }

     //这里很重要 后面DefaultFilterChainManager filterChains属性在这里添加值
        manager.createDefaultChain("/**");
        return manager;
    }

    protected AbstractShiroFilter createInstance() throws Exception {
        log.debug("Creating Shiro Filter instance.");
        SecurityManager securityManager = this.getSecurityManager();
        String msg;
        if (securityManager == null) {
            msg = "SecurityManager property must be set.";
            throw new BeanInitializationException(msg);
        } else if (!(securityManager instanceof WebSecurityManager)) {
            msg = "The security manager does not implement the WebSecurityManager interface.";
            throw new BeanInitializationException(msg);
        } else {

           //3、调用createFilterChainManager()
            FilterChainManager manager = this.createFilterChainManager();
            PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();

            //6、将manager注入到chainResolver中,此时的manager有子过滤器,因而chainResolver也是可以获取到子过滤器的
            chainResolver.setFilterChainManager(manager);

       //7、返回SpringShiroFilter对象,即向Spring容器中注入SpringShiroFilter bean对象(带chain)
            return new ShiroFilterFactoryBean.SpringShiroFilter((WebSecurityManager)securityManager, chainResolver);
        }
    }

   //.........删除无关代码

    private void applyGlobalPropertiesIfNecessary(Filter filter) {
        this.applyLoginUrlIfNecessary(filter);
        this.applySuccessUrlIfNecessary(filter);
        this.applyUnauthorizedUrlIfNecessary(filter);
    }

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Filter) {
            log.debug("Found filter chain candidate filter \'{}\'", beanName);
            Filter filter = (Filter)bean;
            this.applyGlobalPropertiesIfNecessary(filter);
            this.getFilters().put(beanName, filter);
        } else {
            log.trace("Ignoring non-Filter bean \'{}\'", beanName);
        }

        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    private static final class SpringShiroFilter extends AbstractShiroFilter {
        protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
            if (webSecurityManager == null) {
                throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
            } else {
                this.setSecurityManager(webSecurityManager);
                if (resolver != null) {
                    this.setFilterChainResolver(resolver);
                }

            }
        }
    }
}

 

 

三、FilterRegistrationBean:上面两步已经将BeanName为shiroFilter的SpringShiroFilter注入到了Spring容器中,现在来说一下是如何从Spring容器中注册到Servlet容器中的。

一般我们在Spring中配置Filter是这样写的:

@Configuration
public class FilterConfig {
    /**
     * 注册DelegatingFilterProxy(Shiro)
     *
     * @return
     * @author 
     * @create 2020年8月19日
     */
    @Bean
    public FilterRegistrationBean shiroFilterRegistrationBean () {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
        //  该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
        filterRegistration.addInitParameter("targetFilterLifecycle", "true");
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns("/*");
        return filterRegistration;
    }
}

 

 他具体的工作原理是什么?

1、Spring容器在启动时会调用org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext的selfInitialize(ServletContext servletContext)方法。
    private void selfInitialize(ServletContext servletContext) throws ServletException {
        this.prepareWebApplicationContext(servletContext);
        this.registerApplicationScope(servletContext);
        WebApplicationContextUtils.registerEnvironmentBeans(this.getBeanFactory(), servletContext);
        Iterator var2 = this.getServletContextInitializerBeans().iterator();

        while(var2.hasNext()) {
            ServletContextInitializer beans = (ServletContextInitializer)var2.next();
            beans.onStartup(servletContext);
        }

    }

 

 2、bean.onStartup(servletContext)调用 RegistrationBean.onStartup()方法,该方法又调用抽象方法register()。

 public final void onStartup(ServletContext servletContext) throws ServletException {
        String description = this.getDescription();
        if (!this.isEnabled()) {
            logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
        } else {
            this.register(description, servletContext);
        }
    }

 protected abstract void register(String description, ServletContext servletContext);

3、register()方法由RegistrationBean的子类DynamicRegistrationBean实现,并调用由子类AbstractFilterRegistrationBean实现的addRegistration()方法。

  protected final void register(String description, ServletContext servletContext) {
        D registration = this.addRegistration(description, servletContext);
        if (registration == null) {
            logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
        } else {
            this.configure(registration);
        }
    }

    protected abstract D addRegistration(String description, ServletContext servletContext);

 

 4、AbstractFilterRegistrationBean的addRegistration()方法,向ServletContext中注册了一个过滤器。而这个过滤器是子类提供的,也就是我们在Spring中注入的FilterRegistrationBean提供的,他的getFilter()方法

返回的就是我们的shiroFilter

AbstractFilterRegistrationBean:

   protected Dynamic addRegistration(String description, ServletContext servletContext) {
        //调用子类实现的抽象方法
        Filter filter = this.getFilter();
        //向servletContext中注册Filter
        return servletContext.addFilter(this.getOrDeduceName(filter), filter);
    }
    //这个方法由子类实现
    public abstract T getFilter();

FilterRegistrationBean:

public class FilterRegistrationBean<T extends Filter> extends AbstractFilterRegistrationBean<T> {
    private T filter;

    public FilterRegistrationBean() {
        super(new ServletRegistrationBean[0]);
    }

    public FilterRegistrationBean(T filter, ServletRegistrationBean<?>... servletRegistrationBeans) {
        super(servletRegistrationBeans);
        Assert.notNull(filter, "Filter must not be null");
        this.filter = filter;
    }

    public T getFilter() {
        return this.filter;
    }

    public void setFilter(T filter) {
        Assert.notNull(filter, "Filter must not be null");
        this.filter = filter;
    }
}

 

 

至此,shiroFilter从Spring容器中注册到了Servlet容器中了。

以上是关于SpringBoot整合shiro系列-SpingBoot是如何将shiroFilter注册到servlet容器中的的主要内容,如果未能解决你的问题,请参考以下文章

全栈编程系列SpringBoot整合Shiro(含KickoutSessionControlFilter并发在线人数控制以及不生效问题配置启动异常No SecurityManager...)

SpringBoot整合shiro系列-SpingBoot是如何将shiroFilter注册到servlet容器中的

全栈编程系列SpringBoot整合Shiro(含KickoutSessionControlFilter并发在线人数控制以及不生效问题配置启动异常No SecurityManager...)(代码片段

SpringBoot整合Shiro安全框架

SpringBoot进阶之整合Shiro鉴权框架(三)

Springboot整合Shiro