zoukankan      html  css  js  c++  java
  • Spring-shiro源码陶冶-DelegatingFilterProxy和ShiroFilterFactoryBean

    阅读源码有助于陶冶情操,本文旨在简单的分析shiro在Spring中的使用

    介绍

    Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理主要功能;另外其也提供了Web Support、缓存、Remember Me、并发等功能。
    而Shiro的架构核心可看来自Java技术栈提供的图片
    shiro_structure

    对上述的图作简单的描述

    • Subject 用户视图,shiro对用户或者第三方服务、任何需要登录的东西统一称之为Subject对象
    • SecurityManager 安全管理器,其中内含缓存管理、会话管理,主要是对登录过来的Subject视图作一系列的安全操作(包括下述提及的Realms的关联使用)
    • Realms 数据管理器,其主要充当shiro与安全数据交互的桥梁,帮助shiro容器完成用户的校验以及认证功能。可配置多个,但必须配置至少一个。shiro也提供了默认的实现比如ladp/jdbc等方式的用户校验认证

    更多的部分读者可参阅知乎Java技术栈专栏文章非常详尽的 Shiro 架构解析。本文只分析其与Spring如何搭配使用

    web.xml配置Shiro环境

    配置清单如下

     <!-- shiro 安全过滤器 -->
        <filter>
            <filter-name>shiroFilter</filter-name>
    		<!--spring调用shiro代理-->
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
            <async-supported>true</async-supported>
            <init-param>
    			<!--是否开启Filter的生命周期,主要涉及init和destory-->
                <param-name>targetFilterLifecycle</param-name>
                <param-value>true</param-value>
            </init-param>
        </filter>
    
        <filter-mapping>
            <filter-name>shiroFilter</filter-name>
            <url-pattern>/*</url-pattern>
            <dispatcher>REQUEST</dispatcher>
        </filter-mapping>
    

    DelegatingFilterProxy#initFilterBean

    入口方法,首先会执行初始化工作,shiro和spring security的代理加载实体Filter类都是通过此入口方法,代码清单如下

    	@Override
    	protected void initFilterBean() throws ServletException {
    		synchronized (this.delegateMonitor) {
    			if (this.delegate == null) {
    				// If no target bean name specified, use filter name.如果没有指定则采用对应的<filter-name>的值
    				if (this.targetBeanName == null) {
    					this.targetBeanName = getFilterName();
    				}
    				// Fetch Spring root application context and initialize the delegate early,
    				// if possible. If the root application context will be started after this
    				// filter proxy, we'll have to resort to lazy initialization.
    				WebApplicationContext wac = findWebApplicationContext();
    				if (wac != null) {
    					//初始化
    					this.delegate = initDelegate(wac);
    				}
    			}
    		}
    	}
    

    我们主要关心DelegatingFilterProxy#initDelegate初始化委托Filter方法,代码清单如下

    	protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
    		//根据名字获取ApplicationContext环境的bean,且必须是Filter的实现类
    		Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
    		//true则初始化对应的Filter类
    		if (isTargetFilterLifecycle()) {
    			//这里一般对应的Filter类为`org.apache.shiro.spring.web.ShiroFilterFactoryBean`
    			delegate.init(getFilterConfig());
    		}
    		return delegate;
    	}
    

    由以上代码可以确认,application-shiro.xmlSpring配置文件必须含有与web.xmlDelegatingFilterProxy类对应的<filter-name>的bean配置

    示例文件

    	<!-- Shiro的Web过滤器 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    		<!--securityManager属性必须存在,通过安全管理器调用Realm接口实现的认证/授权方法-->
            <property name="securityManager" ref="securityManager"/>
            <!--登录、主页、未通过授权页面的url-->
            <property name="loginUrl" value="/login"/>
            <property name="successUrl" value="/index.html"/>
            <property name="unauthorizedUrl" value="/403.html"/>
            <!--自定义的filter集合-->
           <property name="filters">
                <!--这里采用map集合主要是响应内部属性Map<String, Filter> filters-->
                <util:map>
                    <entry key="authc" value-ref="formAuthenticationFilter"/>
                    <entry key="perm" value-ref="permissionsAuthorizationFilter"/>
                    <entry key="sysUser" value-ref="sysUserFilter"/>
                    <entry key="user" value-ref="userFilter"/>
                </util:map>
            </property>
    	<!--对应路径请求Filter链,=右边代表是filter过滤引用,且是顺序执行,url且从上置下优先匹配,一旦匹配则不往下搜寻-->
    	<!--=右边表达也可为authc[role1,role2]表明访问该url必须通过认证且具有role1、role2的角色权限-->
            <property name="filterChainDefinitions">
                <value>
                    /test/** = anon
                    /login = captcha,authc
                    /index = anon
                    /403.html = anon
                    /login.html = anon
                    /favicon.ico = anon
                    /static/** = anon
                    /index.html=user,sysUser
                    /welcome.html=user,sysUser
                    /** = user,sysUser,perm
                </value>
            </property>
        </bean>
    

    ShiroFilterFactoryBean#createInstance方法返回Filter实例

    通过ShiroFilterFactoryBean#createInstance方法创建对应的Filter实例,我们看下创建的实例是何种人物,代码清单如下

    	//创建实例,实例对象为SpringShiroFilter
    	protected AbstractShiroFilter createInstance() throws Exception {
    
            log.debug("Creating Shiro Filter instance.");
    		//<property name="securityManager">属性必须存在
            SecurityManager securityManager = getSecurityManager();
            if (securityManager == null) {
                String msg = "SecurityManager property must be set.";
                throw new BeanInitializationException(msg);
            }
    		//securityManager的实现类必须是WebSecurityManager的实现类,表明Spring的shiro结合是web方面的
            if (!(securityManager instanceof WebSecurityManager)) {
                String msg = "The security manager does not implement the WebSecurityManager interface.";
                throw new BeanInitializationException(msg);
            }
    		//filter过滤链管理类创建
            FilterChainManager manager = createFilterChainManager();
    
            //Expose the constructed FilterChainManager by first wrapping it in a
            // FilterChainResolver implementation. The AbstractShiroFilter implementations
            // do not know about FilterChainManagers - only resolvers:
            PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
            chainResolver.setFilterChainManager(manager);
    
            //Now create a concrete ShiroFilter instance and apply the acquired SecurityManager and built
            //FilterChainResolver.  It doesn't matter that the instance is an anonymous inner class
            //here - we're just using it because it is a concrete AbstractShiroFilter instance that accepts
            //injection of the SecurityManager and FilterChainResolver:
            return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
        }
    

    其中涉及filter过滤链管理类的创建,简单分析下,代码清单如下

    	protected FilterChainManager createFilterChainManager() {
    		//采用默认filter过滤链管理类
            DefaultFilterChainManager manager = new DefaultFilterChainManager();
    		//获取默认Filter类集合
            Map<String, Filter> defaultFilters = manager.getFilters();
            //apply global settings if necessary:
            for (Filter filter : defaultFilters.values()) {
    	   //主要设置loginUrl、successUrl、unauthorizedUrl
    	   //即权限Filter类设置loginUrl;认证Filter类设置successUrl、loginUrl;授权Filter类设置unauthorizedUrl,loginUrl
                applyGlobalPropertiesIfNecessary(filter);
            }
    
            //Apply the acquired and/or configured filters:用户自定义的Filter类,通过<property name="filters">设定的
            Map<String, Filter> filters = getFilters();
            if (!CollectionUtils.isEmpty(filters)) {
                for (Map.Entry<String, Filter> entry : filters.entrySet()) {
                    String name = entry.getKey();
                    Filter filter = entry.getValue();
    				//同样设置url属性
                    applyGlobalPropertiesIfNecessary(filter);
    				//设置名字
                    if (filter instanceof Nameable) {
                        ((Nameable) filter).setName(name);
                    }
                    //'init' argument is false, since Spring-configured filters should be initialized
                    //in Spring (i.e. 'init-method=blah') or implement InitializingBean:
                    manager.addFilter(name, filter, false);
                }
            }
    
            //build up the chains: url与对应的filter过滤链配置,解析的是<property name="filterChainDefinitions">属性
    		//url可对应多个filter,且保存filter是通过ArrayList来保存的,表明过滤链则由写的先后顺序执行
            Map<String, String> chains = getFilterChainDefinitionMap();
            if (!CollectionUtils.isEmpty(chains)) {
                for (Map.Entry<String, String> entry : chains.entrySet()) {
                    String url = entry.getKey();
    				//chainDefinition可能为authc[role1,role2]或者authc,anno等
                    String chainDefinition = entry.getValue();
    				//解析以上表达式并保存至相应的自定义filter对象中
                    manager.createChain(url, chainDefinition);
                }
            }
    
            return manager;
        }
    

    总结

    1. Spring容器中使用Apache Shiro,需要配置

      • web.xml中配置节点,节点类为org.springframework.web.filter.DelegatingFilterProxy
      • spring配置shiro文件中需要节点,节点id名必须与web.xml定义中的节点的属性一致,且beanClass为org.apache.shiro.spring.web.ShiroFilterFactoryBean
    2. Spring配置文件中配置org.apache.shiro.spring.web.ShiroFilterFactoryBean主要设置shiro的相关filter用于拦截请求,其中的属性含义见本文的示例文件说明

    下节预告

    Spring-shiro源码陶冶-DefaultFilter

  • 相关阅读:
    关于oracle的导入数据流程,以及错误解决
    解决 lombok 和 freemarker 下载慢问题 以及安装方法
    解决maven项目没有Maven Dependencies
    将maven仓库改为阿里仓库
    Dos攻击和校网渗透
    KaliLinux切换python版本
    Kali国内更新源
    linux安装jdk(.rpm)
    Centos 关于 mysql 命令
    Linux删除命令
  • 原文地址:https://www.cnblogs.com/question-sky/p/6783060.html
Copyright © 2011-2022 走看看