• Shiro源码(一)-启动原理


    0. 核心概念

    Shiro 的架构图如下:

    几个核心概念如下:

    subject:主体,可以是用户也可以是程序,主体要访问系统,系统需要对主体进行认证、授权。

    securityManager:安全管理器,主体进行认证和授权都 是通过securityManager进行。

    authenticator:认证器,主体进行认证最终通过authenticator进行的。 Authentication - 认证(也就是验证身份合法性)

    authorizer:授权器,主体进行授权最终通过authorizer进行的。Authorization - 鉴权(也就是判断是否有相应的权限)

    sessionManager:web应用中一般是用web容器对session进行管理,shiro也提供一套session管理的方式。

    SessionDao: 通过SessionDao管理session数据,针对个性化的session数据存储需要使用sessionDao。

    cache Manager:缓存管理器,主要对session和授权数据进行缓存,比如将授权数据通过cacheManager进行缓存管理,和ehcache整合对缓存数据进行管理。

    realm:域,领域,相当于数据源,通过realm存取认证、授权相关数据。注意:在realm中存储授权和认证的逻辑。

    1. 自动配置

    1. pom 引入如下配置:

            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.5.3</version>
            </dependency>

    2. 自定义Realm

    import com.zd.bx.bean.user.User;
    import com.zd.bx.service.user.TokenService;
    import com.zd.bx.utils.permission.PermissionUtils;
    import com.zd.bx.utils.shiro.Token;
    import com.zd.bx.utils.system.SpringBootUtils;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class CustomRealm extends AuthorizingRealm {
    
        private static final Logger log = LoggerFactory.getLogger(CustomRealm.class);
    
        /**
         * 鉴权
         *
         * @param principalCollection
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            // getPrimaryPrincipal获取到的是doGetAuthenticationInfo方法最后存进去的user对象
            Object primaryPrincipal = principalCollection.getPrimaryPrincipal();
            if (primaryPrincipal == null) {
                return null;
            }
    
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
            User currentUser = (User) primaryPrincipal;
            // 添加角色
            authorizationInfo.addRoles(PermissionUtils.listUserRolenames(currentUser));
            // 添加权限
            authorizationInfo.addStringPermissions(PermissionUtils.listUserPermissionCodes(currentUser));
    
            log.debug("authorizationInfo roles: {}, permissions: {}", authorizationInfo.getRoles(),
                    authorizationInfo.getStringPermissions());
            return authorizationInfo;
        }
    
        /**
         * 认证
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
                throws AuthenticationException {
    
            if (authenticationToken == null) {
                throw new IncorrectCredentialsException("token失效,请重新登录");
            }
    
            Token token = (Token) authenticationToken;
            TokenService tokenService = SpringBootUtils.getBean(TokenService.class);
            User user = tokenService.getUser(token);
            return new SimpleAuthenticationInfo(user, token.getCredentials(), this.getName());
        }
    
        // 清除缓存
        public void clearCache() {
            PrincipalCollection principalCollection = SecurityUtils.getSubject().getPrincipals();
            super.clearCache(principalCollection);
        }
    }

    3. 编写Configuration 配置文件注入相关bean 到Spring 中

    import com.zd.bx.utils.file.PropertiesFileUtils;
    import org.apache.commons.collections4.CollectionUtils;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Properties;
    
    @Configuration
    public class ShiroConfig {
    
        private static final Map<String, String> FILTER_CHAIN_DEFINITION_MAP = new HashMap<>();
    
        static {
            initFilterChainDefinitionMap();
        }
    
        private static void initFilterChainDefinitionMap() {
            // 加载配置在permission.properties文件中的配置
            Properties properties = PropertiesFileUtils.getProperties("permission.properties");
            if (properties != null && CollectionUtils.isNotEmpty(properties.entrySet())) {
                Iterator<Entry<Object, Object>> iterator = properties.entrySet().iterator();
                while (iterator.hasNext()) {
                    Entry<Object, Object> next = iterator.next();
                    String key = next.getKey().toString();
                    String value = next.getValue().toString();
                    FILTER_CHAIN_DEFINITION_MAP.put(key, value);
                }
            }
    
            /**
             *  路径 -> 过滤器名称1[参数1,参数2,参数3...],过滤器名称2[参数1,参数2...]...
             * 自定义配置(前面是路径, 后面是具体的过滤器名称加参数,多个用逗号进行分割,过滤器参数也多个之间也是用逗号分割))
             * 有的过滤器不需要参数,比如anon, authc, shiro 在解析的时候接默认解析一个数组为 [name, null]
             */
            FILTER_CHAIN_DEFINITION_MAP.put("/test2", "anon"); // 测试地址
            FILTER_CHAIN_DEFINITION_MAP.put("/user/**", "roles[系统管理员,用户管理员],perms['user:manager:*']");
            FILTER_CHAIN_DEFINITION_MAP.put("/**", "authc"); // 所有资源都需要经过验证
        }
    
        // 将自己的验证方式加入容器
        @Bean
        public CustomRealm myShiroRealm() {
            CustomRealm customRealm = new CustomRealm();
            return customRealm;
        }
    
        // 权限管理,配置主要是Realm的管理认证
        @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(myShiroRealm());
            return securityManager;
        }
    
        // Filter工厂,设置对应的过滤条件和跳转条件
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager);
    
            // 登录页和成功跳转页面不需要设置(前后端不分离项目可以写)
            // factoryBean.setLoginUrl("/shiro/login.html");
            // factoryBean.setSuccessUrl("/shiro/index.html");
    
            // 自定义需要权限验证的过滤器。对于前后端分离的项目可以自定重写这个filter
    //        Map<String, Filter> filterMaps = new HashMap<>();
    //        filterMaps.put("authc", new ShiroAuthFilter());
    //        shiroFilterFactoryBean.setFilters(filterMaps);
    
            // 定义处理规则
            shiroFilterFactoryBean.setFilterChainDefinitionMap(setFilterChainDefinitionMap());
    
            return shiroFilterFactoryBean;
        }
    
        private Map<String, String> setFilterChainDefinitionMap() {
            return FILTER_CHAIN_DEFINITION_MAP;
        }
    }

    4. 编写测试Controller

    import com.zd.bx.bean.user.Permission;
    import com.zd.bx.service.user.PermissionService;
    import org.apache.shiro.authz.annotation.RequiresPermissions;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class TestController {
    
        @RequiresPermissions(value = {"test"})
        @GetMapping("/test")
        public String test() {
            return "test";
        }
    
        @Autowired
        private PermissionService permissionService;
    
        @GetMapping("/test2")
        public String test2() {
            Permission permissions = permissionService.selectByUniqueCode("2aeb7756-62f7-49ba-aa41-31e1bbc87040");
            Permission permissions2 = permissionService.selectByUniqueCode("2aeb7756-62f7-49ba-aa41-31e1bbc87040");
            int i = permissionService.selectCount(null);
            System.out.println(permissions);
            System.out.println(permissions2);
            System.out.println(i);
            return "test";
        }
    }

    5. 测试

    (1) 测试可以匿名访问的连接:

    $ curl http://localhost:8081/test2
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100     4  100     4    0     0    129      0 --:--:-- --:--:-- --:--:--   137test

    (2) 访问需要鉴权的连接 /test

    页面会被重定向到:/login.jsp

      如果想设置登录页面、未授权页面、登录成功页面, 可以通过上面 ShiroFilterFactoryBean 进行设置。 到这里基本的功能可以使用。下面研究其执行原理。

    2. 源码剖析

      我们在Configuration 中注入的对象有三个CustomRealm、SecurityManager、ShiroFilterFactoryBean。 前面两个对象可以说是shiro必须的组件,realm 注入到Securitymanager 中; SecurityManager 注入到了 ShiroFilterFactoryBean 中。

    1. SecurityManager 内部包含的相关属性

    如下代码:

        @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(myShiroRealm());
            return securityManager;
        }

    1》这里设置了一个自定义的Realm,也可以调用 setRealms 设置多个Realm.

    2》DefaultWebSecurityManager 构造查看

    其继承关系如下:

    1》  从CachingSecurityManager 开始查看其内部重要属性以及方法:

    public abstract class CachingSecurityManager implements SecurityManager, Destroyable, CacheManagerAware, EventBusAware {
        private CacheManager cacheManager;
        private EventBus eventBus;

      这里有缓存管理器相关。

    2》RealmSecurityManager

    public abstract class RealmSecurityManager extends CachingSecurityManager {
        private Collection<Realm> realms;
    
        public RealmSecurityManager() {
        }
    
        public void setRealm(Realm realm) {
            if (realm == null) {
                throw new IllegalArgumentException("Realm argument cannot be null");
            } else {
                Collection<Realm> realms = new ArrayList(1);
                realms.add(realm);
                this.setRealms(realms);
            }
        }
    
        public void setRealms(Collection<Realm> realms) {
            if (realms == null) {
                throw new IllegalArgumentException("Realms collection argument cannot be null.");
            } else if (realms.isEmpty()) {
                throw new IllegalArgumentException("Realms collection argument cannot be empty.");
            } else {
                this.realms = realms;
                this.afterRealmsSet();
            }
        }

       有设置Realm 相关的API

    3》 org.apache.shiro.mgt.AuthenticatingSecurityManager

    public abstract class AuthenticatingSecurityManager extends RealmSecurityManager {
        private Authenticator authenticator = new ModularRealmAuthenticator();

       包含一个默认的认证器。

    4》org.apache.shiro.mgt.AuthorizingSecurityManager

    public abstract class AuthorizingSecurityManager extends AuthenticatingSecurityManager {
        private Authorizer authorizer = new ModularRealmAuthorizer();

      包含一个默认的鉴权器

    5》org.apache.shiro.mgt.SessionsSecurityManager

    public abstract class SessionsSecurityManager extends AuthorizingSecurityManager {
        private SessionManager sessionManager = new DefaultSessionManager();

      包含一个默认的session 管理器

    6》 org.apache.shiro.mgt.DefaultSecurityManager

    public class DefaultSecurityManager extends SessionsSecurityManager {
        private static final Logger log = LoggerFactory.getLogger(DefaultSecurityManager.class);
        protected RememberMeManager rememberMeManager;
        protected SubjectDAO subjectDAO;
        protected SubjectFactory subjectFactory;
    
        public DefaultSecurityManager() {
            this.subjectFactory = new DefaultSubjectFactory();
            this.subjectDAO = new DefaultSubjectDAO();
        }
    
        public DefaultSecurityManager(Realm singleRealm) {
            this();
            this.setRealm(singleRealm);
        }
    
        public DefaultSecurityManager(Collection<Realm> realms) {
            this();
            this.setRealms(realms);
        }

      包含两个重要属性:RememberMeManager、 SubjectFactory

    7》 org.apache.shiro.web.mgt.DefaultWebSecurityManager#DefaultWebSecurityManager

        public DefaultWebSecurityManager() {
            super();
            DefaultWebSessionStorageEvaluator webEvalutator = new DefaultWebSessionStorageEvaluator();  
            ((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(webEvalutator);
            this.sessionMode = HTTP_SESSION_MODE;
            setSubjectFactory(new DefaultWebSubjectFactory());
            setRememberMeManager(new CookieRememberMeManager());
            setSessionManager(new ServletContainerSessionManager());
            webEvalutator.setSessionManager(getSessionManager());
        }

      从上面的继承链可以看到我们的SecurityManager 通过一系列的继承可以具有: cache管理、realm管理器、认证器、鉴权器、session 管理器等重要功能。

    2. ShiroFilterFactoryBean 查看

      可以看到这个类是一个FactoryBean, 其注入到Spring 内部的是getOabject 返回的对象。

    org.apache.shiro.spring.web.ShiroFilterFactoryBean 源码如下:

    /*
     * 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.spring.web;
    
    import org.apache.shiro.config.Ini;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.util.CollectionUtils;
    import org.apache.shiro.util.Nameable;
    import org.apache.shiro.util.StringUtils;
    import org.apache.shiro.web.config.IniFilterChainResolverFactory;
    import org.apache.shiro.web.filter.AccessControlFilter;
    import org.apache.shiro.web.filter.authc.AuthenticationFilter;
    import org.apache.shiro.web.filter.authz.AuthorizationFilter;
    import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
    import org.apache.shiro.web.filter.mgt.FilterChainManager;
    import org.apache.shiro.web.filter.mgt.FilterChainResolver;
    import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
    import org.apache.shiro.web.mgt.WebSecurityManager;
    import org.apache.shiro.web.servlet.AbstractShiroFilter;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanInitializationException;
    import org.springframework.beans.factory.FactoryBean;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    import javax.servlet.Filter;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * {@link org.springframework.beans.factory.FactoryBean FactoryBean} to be used in Spring-based web applications for
     * defining the master Shiro Filter.
     * <h4>Usage</h4>
     * Declare a DelegatingFilterProxy in {@code web.xml}, matching the filter name to the bean id:
     * <pre>
     * &lt;filter&gt;
     *   &lt;filter-name&gt;<b>shiroFilter</b>&lt;/filter-name&gt;
     *   &lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&lt;filter-class&gt;
     *   &lt;init-param&gt;
     *    &lt;param-name&gt;targetFilterLifecycle&lt;/param-name&gt;
     *     &lt;param-value&gt;true&lt;/param-value&gt;
     *   &lt;/init-param&gt;
     * &lt;/filter&gt;
     * </pre>
     * Then, in your spring XML file that defines your web ApplicationContext:
     * <pre>
     * &lt;bean id="<b>shiroFilter</b>" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"&gt;
     *    &lt;property name="securityManager" ref="securityManager"/&gt;
     *    &lt;!-- other properties as necessary ... --&gt;
     * &lt;/bean&gt;
     * </pre>
     * <h4>Filter Auto-Discovery</h4>
     * While there is a {@link #setFilters(java.util.Map) filters} property that allows you to assign a filter beans
     * to the 'pool' of filters available when defining {@link #setFilterChainDefinitions(String) filter chains}, it is
     * optional.
     * <p/>
     * This implementation is also a {@link BeanPostProcessor} and will acquire
     * any {@link javax.servlet.Filter Filter} beans defined independently in your Spring application context.  Upon
     * discovery, they will be automatically added to the {@link #setFilters(java.util.Map) map} keyed by the bean ID.
     * That ID can then be used in the filter chain definitions, for example:
     *
     * <pre>
     * &lt;bean id="<b>myCustomFilter</b>" class="com.class.that.implements.javax.servlet.Filter"/&gt;
     * ...
     * &lt;bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"&gt;
     *    ...
     *    &lt;property name="filterChainDefinitions"&gt;
     *        &lt;value&gt;
     *            /some/path/** = authc, <b>myCustomFilter</b>
     *        &lt;/value&gt;
     *    &lt;/property&gt;
     * &lt;/bean&gt;
     * </pre>
     * <h4>Global Property Values</h4>
     * Most Shiro servlet Filter implementations exist for defining custom Filter
     * {@link #setFilterChainDefinitions(String) chain definitions}.  Most implementations subclass one of the
     * {@link AccessControlFilter}, {@link AuthenticationFilter}, {@link AuthorizationFilter} classes to simplify things,
     * and each of these 3 classes has configurable properties that are application-specific.
     * <p/>
     * A dilemma arises where, if you want to for example set the application's 'loginUrl' for any Filter, you don't want
     * to have to manually specify that value for <em>each</em> filter instance definied.
     * <p/>
     * To prevent configuration duplication, this implementation provides the following properties to allow you
     * to set relevant values in only one place:
     * <ul>
     * <li>{@link #setLoginUrl(String)}</li>
     * <li>{@link #setSuccessUrl(String)}</li>
     * <li>{@link #setUnauthorizedUrl(String)}</li>
     * </ul>
     *
     * Then at startup, any values specified via these 3 properties will be applied to all configured
     * Filter instances so you don't have to specify them individually on each filter instance.  To ensure your own custom
     * filters benefit from this convenience, your filter implementation should subclass one of the 3 mentioned
     * earlier.
     *
     * @see org.springframework.web.filter.DelegatingFilterProxy DelegatingFilterProxy
     * @since 1.0
     */
    public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor {
    
        private static transient final Logger log = LoggerFactory.getLogger(ShiroFilterFactoryBean.class);
    
        private SecurityManager securityManager;
    
        private Map<String, Filter> filters;
    
        private Map<String, String> filterChainDefinitionMap; //urlPathExpression_to_comma-delimited-filter-chain-definition
    
        private String loginUrl;
        private String successUrl;
        private String unauthorizedUrl;
    
        private AbstractShiroFilter instance;
    
        public ShiroFilterFactoryBean() {
            this.filters = new LinkedHashMap<String, Filter>();
            this.filterChainDefinitionMap = new LinkedHashMap<String, String>(); //order matters!
        }
    
        /**
         * Sets the application {@code SecurityManager} instance to be used by the constructed Shiro Filter.  This is a
         * required property - failure to set it will throw an initialization exception.
         *
         * @return the application {@code SecurityManager} instance to be used by the constructed Shiro Filter.
         */
        public SecurityManager getSecurityManager() {
            return securityManager;
        }
    
        /**
         * Sets the application {@code SecurityManager} instance to be used by the constructed Shiro Filter.  This is a
         * required property - failure to set it will throw an initialization exception.
         *
         * @param securityManager the application {@code SecurityManager} instance to be used by the constructed Shiro Filter.
         */
        public void setSecurityManager(SecurityManager securityManager) {
            this.securityManager = securityManager;
        }
    
        /**
         * Returns the application's login URL to be assigned to all acquired Filters that subclass
         * {@link AccessControlFilter} or {@code null} if no value should be assigned globally. The default value
         * is {@code null}.
         *
         * @return the application's login URL to be assigned to all acquired Filters that subclass
         *         {@link AccessControlFilter} or {@code null} if no value should be assigned globally.
         * @see #setLoginUrl
         */
        public String getLoginUrl() {
            return loginUrl;
        }
    
        /**
         * Sets the application's login URL to be assigned to all acquired Filters that subclass
         * {@link AccessControlFilter}.  This is a convenience mechanism: for all configured {@link #setFilters filters},
         * as well for any default ones ({@code authc}, {@code user}, etc), this value will be passed on to each Filter
         * via the {@link AccessControlFilter#setLoginUrl(String)} method<b>*</b>.  This eliminates the need to
         * configure the 'loginUrl' property manually on each filter instance, and instead that can be configured once
         * via this attribute.
         * <p/>
         * <b>*</b>If a filter already has already been explicitly configured with a value, it will
         * <em>not</em> receive this value. Individual filter configuration overrides this global convenience property.
         *
         * @param loginUrl the application's login URL to apply to as a convenience to all discovered
         *                 {@link AccessControlFilter} instances.
         * @see AccessControlFilter#setLoginUrl(String)
         */
        public void setLoginUrl(String loginUrl) {
            this.loginUrl = loginUrl;
        }
    
        /**
         * Returns the application's after-login success URL to be assigned to all acquired Filters that subclass
         * {@link AuthenticationFilter} or {@code null} if no value should be assigned globally. The default value
         * is {@code null}.
         *
         * @return the application's after-login success URL to be assigned to all acquired Filters that subclass
         *         {@link AuthenticationFilter} or {@code null} if no value should be assigned globally.
         * @see #setSuccessUrl
         */
        public String getSuccessUrl() {
            return successUrl;
        }
    
        /**
         * Sets the application's after-login success URL to be assigned to all acquired Filters that subclass
         * {@link AuthenticationFilter}.  This is a convenience mechanism: for all configured {@link #setFilters filters},
         * as well for any default ones ({@code authc}, {@code user}, etc), this value will be passed on to each Filter
         * via the {@link AuthenticationFilter#setSuccessUrl(String)} method<b>*</b>.  This eliminates the need to
         * configure the 'successUrl' property manually on each filter instance, and instead that can be configured once
         * via this attribute.
         * <p/>
         * <b>*</b>If a filter already has already been explicitly configured with a value, it will
         * <em>not</em> receive this value. Individual filter configuration overrides this global convenience property.
         *
         * @param successUrl the application's after-login success URL to apply to as a convenience to all discovered
         *                   {@link AccessControlFilter} instances.
         * @see AuthenticationFilter#setSuccessUrl(String)
         */
        public void setSuccessUrl(String successUrl) {
            this.successUrl = successUrl;
        }
    
        /**
         * Returns the application's after-login success URL to be assigned to all acquired Filters that subclass
         * {@link AuthenticationFilter} or {@code null} if no value should be assigned globally. The default value
         * is {@code null}.
         *
         * @return the application's after-login success URL to be assigned to all acquired Filters that subclass
         *         {@link AuthenticationFilter} or {@code null} if no value should be assigned globally.
         * @see #setSuccessUrl
         */
        public String getUnauthorizedUrl() {
            return unauthorizedUrl;
        }
    
        /**
         * Sets the application's 'unauthorized' URL to be assigned to all acquired Filters that subclass
         * {@link AuthorizationFilter}.  This is a convenience mechanism: for all configured {@link #setFilters filters},
         * as well for any default ones ({@code roles}, {@code perms}, etc), this value will be passed on to each Filter
         * via the {@link AuthorizationFilter#setUnauthorizedUrl(String)} method<b>*</b>.  This eliminates the need to
         * configure the 'unauthorizedUrl' property manually on each filter instance, and instead that can be configured once
         * via this attribute.
         * <p/>
         * <b>*</b>If a filter already has already been explicitly configured with a value, it will
         * <em>not</em> receive this value. Individual filter configuration overrides this global convenience property.
         *
         * @param unauthorizedUrl the application's 'unauthorized' URL to apply to as a convenience to all discovered
         *                        {@link AuthorizationFilter} instances.
         * @see AuthorizationFilter#setUnauthorizedUrl(String)
         */
        public void setUnauthorizedUrl(String unauthorizedUrl) {
            this.unauthorizedUrl = unauthorizedUrl;
        }
    
        /**
         * Returns the filterName-to-Filter map of filters available for reference when defining filter chain definitions.
         * All filter chain definitions will reference filters by the names in this map (i.e. the keys).
         *
         * @return the filterName-to-Filter map of filters available for reference when defining filter chain definitions.
         */
        public Map<String, Filter> getFilters() {
            return filters;
        }
    
        /**
         * Sets the filterName-to-Filter map of filters available for reference when creating
         * {@link #setFilterChainDefinitionMap(java.util.Map) filter chain definitions}.
         * <p/>
         * <b>Note:</b> This property is optional:  this {@code FactoryBean} implementation will discover all beans in the
         * web application context that implement the {@link Filter} interface and automatically add them to this filter
         * map under their bean name.
         * <p/>
         * For example, just defining this bean in a web Spring XML application context:
         * <pre>
         * &lt;bean id=&quot;myFilter&quot; class=&quot;com.class.that.implements.javax.servlet.Filter&quot;&gt;
         * ...
         * &lt;/bean&gt;</pre>
         * Will automatically place that bean into this Filters map under the key '<b>myFilter</b>'.
         *
         * @param filters the optional filterName-to-Filter map of filters available for reference when creating
         *                {@link #setFilterChainDefinitionMap (java.util.Map) filter chain definitions}.
         */
        public void setFilters(Map<String, Filter> filters) {
            this.filters = filters;
        }
    
        /**
         * Returns the chainName-to-chainDefinition map of chain definitions to use for creating filter chains intercepted
         * by the Shiro Filter.  Each map entry should conform to the format defined by the
         * {@link FilterChainManager#createChain(String, String)} JavaDoc, where the map key is the chain name (e.g. URL
         * path expression) and the map value is the comma-delimited string chain definition.
         *
         * @return he chainName-to-chainDefinition map of chain definitions to use for creating filter chains intercepted
         *         by the Shiro Filter.
         */
        public Map<String, String> getFilterChainDefinitionMap() {
            return filterChainDefinitionMap;
        }
    
        /**
         * Sets the chainName-to-chainDefinition map of chain definitions to use for creating filter chains intercepted
         * by the Shiro Filter.  Each map entry should conform to the format defined by the
         * {@link FilterChainManager#createChain(String, String)} JavaDoc, where the map key is the chain name (e.g. URL
         * path expression) and the map value is the comma-delimited string chain definition.
         *
         * @param filterChainDefinitionMap the chainName-to-chainDefinition map of chain definitions to use for creating
         *                                 filter chains intercepted by the Shiro Filter.
         */
        public void setFilterChainDefinitionMap(Map<String, String> filterChainDefinitionMap) {
            this.filterChainDefinitionMap = filterChainDefinitionMap;
        }
    
        /**
         * A convenience method that sets the {@link #setFilterChainDefinitionMap(java.util.Map) filterChainDefinitionMap}
         * property by accepting a {@link java.util.Properties Properties}-compatible string (multi-line key/value pairs).
         * Each key/value pair must conform to the format defined by the
         * {@link FilterChainManager#createChain(String,String)} JavaDoc - each property key is an ant URL
         * path expression and the value is the comma-delimited chain definition.
         *
         * @param definitions a {@link java.util.Properties Properties}-compatible string (multi-line key/value pairs)
         *                    where each key/value pair represents a single urlPathExpression-commaDelimitedChainDefinition.
         */
        public void setFilterChainDefinitions(String definitions) {
            Ini ini = new Ini();
            ini.load(definitions);
            //did they explicitly state a 'urls' section?  Not necessary, but just in case:
            Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
            if (CollectionUtils.isEmpty(section)) {
                //no urls section.  Since this _is_ a urls chain definition property, just assume the
                //default section contains only the definitions:
                section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
            }
            setFilterChainDefinitionMap(section);
        }
    
        /**
         * Lazily creates and returns a {@link AbstractShiroFilter} concrete instance via the
         * {@link #createInstance} method.
         *
         * @return the application's Shiro Filter instance used to filter incoming web requests.
         * @throws Exception if there is a problem creating the {@code Filter} instance.
         */
        public Object getObject() throws Exception {
            if (instance == null) {
                instance = createInstance();
            }
            return instance;
        }
    
        /**
         * Returns <code>{@link org.apache.shiro.web.servlet.AbstractShiroFilter}.class</code>
         *
         * @return <code>{@link org.apache.shiro.web.servlet.AbstractShiroFilter}.class</code>
         */
        public Class getObjectType() {
            return SpringShiroFilter.class;
        }
    
        /**
         * Returns {@code true} always.  There is almost always only ever 1 Shiro {@code Filter} per web application.
         *
         * @return {@code true} always.  There is almost always only ever 1 Shiro {@code Filter} per web application.
         */
        public boolean isSingleton() {
            return true;
        }
    
        protected FilterChainManager createFilterChainManager() {
    
            DefaultFilterChainManager manager = new DefaultFilterChainManager();
            Map<String, Filter> defaultFilters = manager.getFilters();
            //apply global settings if necessary:
            for (Filter filter : defaultFilters.values()) {
                applyGlobalPropertiesIfNecessary(filter);
            }
    
            //Apply the acquired and/or configured 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();
                    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:
            Map<String, String> chains = getFilterChainDefinitionMap();
            if (!CollectionUtils.isEmpty(chains)) {
                for (Map.Entry<String, String> entry : chains.entrySet()) {
                    String url = entry.getKey();
                    String chainDefinition = entry.getValue();
                    manager.createChain(url, chainDefinition);
                }
            }
    
            return manager;
        }
    
        /**
         * This implementation:
         * <ol>
         * <li>Ensures the required {@link #setSecurityManager(org.apache.shiro.mgt.SecurityManager) securityManager}
         * property has been set</li>
         * <li>{@link #createFilterChainManager() Creates} a {@link FilterChainManager} instance that reflects the
         * configured {@link #setFilters(java.util.Map) filters} and
         * {@link #setFilterChainDefinitionMap(java.util.Map) filter chain definitions}</li>
         * <li>Wraps the FilterChainManager with a suitable
         * {@link org.apache.shiro.web.filter.mgt.FilterChainResolver FilterChainResolver} since the Shiro Filter
         * implementations do not know of {@code FilterChainManager}s</li>
         * <li>Sets both the {@code SecurityManager} and {@code FilterChainResolver} instances on a new Shiro Filter
         * instance and returns that filter instance.</li>
         * </ol>
         *
         * @return a new Shiro Filter reflecting any configured filters and filter chain definitions.
         * @throws Exception if there is a problem creating the AbstractShiroFilter instance.
         */
        protected AbstractShiroFilter createInstance() throws Exception {
    
            log.debug("Creating Shiro Filter instance.");
    
            SecurityManager securityManager = getSecurityManager();
            if (securityManager == null) {
                String msg = "SecurityManager property must be set.";
                throw new BeanInitializationException(msg);
            }
    
            if (!(securityManager instanceof WebSecurityManager)) {
                String msg = "The security manager does not implement the WebSecurityManager interface.";
                throw new BeanInitializationException(msg);
            }
    
            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);
        }
    
        private void applyLoginUrlIfNecessary(Filter filter) {
            String loginUrl = getLoginUrl();
            if (StringUtils.hasText(loginUrl) && (filter instanceof AccessControlFilter)) {
                AccessControlFilter acFilter = (AccessControlFilter) filter;
                //only apply the login url if they haven't explicitly configured one already:
                String existingLoginUrl = acFilter.getLoginUrl();
                if (AccessControlFilter.DEFAULT_LOGIN_URL.equals(existingLoginUrl)) {
                    acFilter.setLoginUrl(loginUrl);
                }
            }
        }
    
        private void applySuccessUrlIfNecessary(Filter filter) {
            String successUrl = getSuccessUrl();
            if (StringUtils.hasText(successUrl) && (filter instanceof AuthenticationFilter)) {
                AuthenticationFilter authcFilter = (AuthenticationFilter) filter;
                //only apply the successUrl if they haven't explicitly configured one already:
                String existingSuccessUrl = authcFilter.getSuccessUrl();
                if (AuthenticationFilter.DEFAULT_SUCCESS_URL.equals(existingSuccessUrl)) {
                    authcFilter.setSuccessUrl(successUrl);
                }
            }
        }
    
        private void applyUnauthorizedUrlIfNecessary(Filter filter) {
            String unauthorizedUrl = getUnauthorizedUrl();
            if (StringUtils.hasText(unauthorizedUrl) && (filter instanceof AuthorizationFilter)) {
                AuthorizationFilter authzFilter = (AuthorizationFilter) filter;
                //only apply the unauthorizedUrl if they haven't explicitly configured one already:
                String existingUnauthorizedUrl = authzFilter.getUnauthorizedUrl();
                if (existingUnauthorizedUrl == null) {
                    authzFilter.setUnauthorizedUrl(unauthorizedUrl);
                }
            }
        }
    
        private void applyGlobalPropertiesIfNecessary(Filter filter) {
            applyLoginUrlIfNecessary(filter);
            applySuccessUrlIfNecessary(filter);
            applyUnauthorizedUrlIfNecessary(filter);
        }
    
        /**
         * Inspects a bean, and if it implements the {@link Filter} interface, automatically adds that filter
         * instance to the internal {@link #setFilters(java.util.Map) filters map} that will be referenced
         * later during filter chain construction.
         */
        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;
                applyGlobalPropertiesIfNecessary(filter);
                getFilters().put(beanName, filter);
            } else {
                log.trace("Ignoring non-Filter bean '{}'", beanName);
            }
            return bean;
        }
    
        /**
         * Does nothing - only exists to satisfy the BeanPostProcessor interface and immediately returns the
         * {@code bean} argument.
         */
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    
        /**
         * Ordinarily the {@code AbstractShiroFilter} must be subclassed to additionally perform configuration
         * and initialization behavior.  Because this {@code FactoryBean} implementation manually builds the
         * {@link AbstractShiroFilter}'s
         * {@link AbstractShiroFilter#setSecurityManager(org.apache.shiro.web.mgt.WebSecurityManager) securityManager} and
         * {@link AbstractShiroFilter#setFilterChainResolver(org.apache.shiro.web.filter.mgt.FilterChainResolver) filterChainResolver}
         * properties, the only thing left to do is set those properties explicitly.  We do that in a simple
         * concrete subclass in the constructor.
         */
        private static final class SpringShiroFilter extends AbstractShiroFilter {
    
            protected SpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
                super();
                if (webSecurityManager == null) {
                    throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
                }
                setSecurityManager(webSecurityManager);
                if (resolver != null) {
                    setFilterChainResolver(resolver);
                }
            }
        }
    }
    View Code

    1. org.apache.shiro.spring.web.ShiroFilterFactoryBean#getObjectType 决定返回对象的类型:SpringShiroFilter。 SpringShiroFilter的继承关系如下:

       可以看到是一个javax.servlet.Filter, 也就是我们常说的filter。 既然是一个filter, 之前在Spring 动态注入filter 的过程中了解过。默认这种方式注入的filter,name 为bean的名称,拦截的url 默认为"/*", 也就是所有请求。

      这里也可以看出,shiro 发挥作用是从这个 SpringShiroFilter 开始的。

    2. getObject 返回给Spring 的对象 org.apache.shiro.spring.web.ShiroFilterFactoryBean#createInstance 代码解读

    (1)  获取到securityManager, 并对其进行验证,类型必须是 WebSecurityManager

    (2)  调用 createFilterChainManager 创建 FilterChainManager, 这个对象是shiro 封装的自己内部过滤的责任链条。

        protected FilterChainManager createFilterChainManager() {
    
            DefaultFilterChainManager manager = new DefaultFilterChainManager();
            Map<String, Filter> defaultFilters = manager.getFilters();
            //apply global settings if necessary:
            for (Filter filter : defaultFilters.values()) {
                applyGlobalPropertiesIfNecessary(filter);
            }
    
            //Apply the acquired and/or configured 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();
                    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:
            Map<String, String> chains = getFilterChainDefinitionMap();
            if (!CollectionUtils.isEmpty(chains)) {
                for (Map.Entry<String, String> entry : chains.entrySet()) {
                    String url = entry.getKey();
                    String chainDefinition = entry.getValue();
                    manager.createChain(url, chainDefinition);
                }
            }
    
            return manager;
        }

    1》 org.apache.shiro.web.filter.mgt.DefaultFilterChainManager#DefaultFilterChainManager() 构造如下:

        public DefaultFilterChainManager() {
            this.filters = new LinkedHashMap<String, Filter>();
            this.filterChains = new LinkedHashMap<String, NamedFilterList>();
            addDefaultFilters(false);
        }
    
        protected void addDefaultFilters(boolean init) {
            for (DefaultFilter defaultFilter : DefaultFilter.values()) {
                addFilter(defaultFilter.name(), defaultFilter.newInstance(), init, false);
            }
        }

    org.apache.shiro.web.filter.mgt.DefaultFilter 枚举类型是shiro 内置的一些的默认过滤器, 我们也可以模仿其中的过滤器进行替换或者增加自己的filter

    import org.apache.shiro.util.ClassUtils;
    import org.apache.shiro.web.filter.authc.*;
    import org.apache.shiro.web.filter.authz.*;
    import org.apache.shiro.web.filter.session.NoSessionCreationFilter;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * Enum representing all of the default Shiro Filter instances available to web applications.  Each filter instance is
     * typically accessible in configuration the {@link #name() name} of the enum constant.
     *
     * @since 1.0
     */
    public enum DefaultFilter {
    
        anon(AnonymousFilter.class),
        authc(FormAuthenticationFilter.class),
        authcBasic(BasicHttpAuthenticationFilter.class),
        authcBearer(BearerHttpAuthenticationFilter.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);
    
        private final Class<? extends Filter> filterClass;
    
        private DefaultFilter(Class<? extends Filter> filterClass) {
            this.filterClass = filterClass;
        }
    
        public Filter newInstance() {
            return (Filter) ClassUtils.newInstance(this.filterClass);
        }
    
        public Class<? extends Filter> getFilterClass() {
            return this.filterClass;
        }
    
        public static Map<String, Filter> createInstanceMap(FilterConfig config) {
            Map<String, Filter> filters = new LinkedHashMap<String, Filter>(values().length);
            for (DefaultFilter defaultFilter : values()) {
                Filter filter = defaultFilter.newInstance();
                if (config != null) {
                    try {
                        filter.init(config);
                    } catch (ServletException e) {
                        String msg = "Unable to correctly init default filter instance of type " +
                                filter.getClass().getName();
                        throw new IllegalStateException(msg, e);
                    }
                }
                filters.put(defaultFilter.name(), filter);
            }
            return filters;
        }
    }

      这里面每个枚举处理一种,比如 anon 是匿名请求都可以访问; authc 是认证后可以看; perms 是有响应权限可以看;  roles 是有相应角色可以看;ssl 是只有https请求可以访问。

    以AnonymousFilter 为例子查看:

    源码如下:

    public class AnonymousFilter extends PathMatchingFilter {
    
        /**
         * Always returns <code>true</code> allowing unchecked access to the underlying path or resource.
         *
         * @return <code>true</code> always, allowing unchecked access to the underlying path or resource.
         */
        @Override
        protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) {
            // Always return true since we allow access to anyone
            return true;
        }
    
    }

    其继承图如下:(可以看出本质也是一个javax.servlet.Filter, 这种filter 没有添加到servlet 环境,那么是如何发挥作用的?下节研究。)

    2》 getFilters() 从org.apache.shiro.spring.web.ShiroFilterFactoryBean#filters 属性中拿自己手动设置的过滤器。 如果是name 和上面默认的一样,会进行覆盖; 如果name 不一样会进行新增。最终新增或者覆盖的还是org.apache.shiro.web.filter.mgt.DefaultFilterChainManager#filters 内部的元素。

        将来如果需要新增自己的filter,可以设置到这个属性中; 如果需要替换已有的filter, 也可以设置到这个属性中,name 和原来的一致就回覆盖。

    3》 getFilterChainDefinitionMap() 获取到自己设置的url 和 过滤器匹配规则,然后调用 org.apache.shiro.web.filter.mgt.DefaultFilterChainManager#createChain 解析每个url 对应的过滤器链条。 

    org.apache.shiro.web.filter.mgt.DefaultFilterChainManager#createChain:

        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 + "]");
            }
    
            //parse the value by tokenizing it to get the resulting filter-specific config entries
            //
            //e.g. for a value of
            //
            //     "authc, roles[admin,user], perms[file:edit]"
            //
            // the resulting token array would equal
            //
            //     { "authc", "roles[admin,user]", "perms[file:edit]" }
            //
            String[] filterTokens = splitChainDefinition(chainDefinition);
    
            //each token is specific to each filter.
            //strip the name and extract any filter-specific config between brackets [ ]
            for (String token : filterTokens) {
                String[] nameConfigPair = toNameConfigPair(token);
    
                //now we have the filter name, path and (possibly null) path-specific config.  Let's apply them:
                addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);
            }
        }
    
        protected String[] splitChainDefinition(String chainDefinition) {
            return StringUtils.split(chainDefinition, StringUtils.DEFAULT_DELIMITER_CHAR, '[', ']', true, true);
        }
    
        protected String[] toNameConfigPair(String token) throws ConfigurationException {
    
            try {
                String[] pair = token.split("\[", 2);
                String name = StringUtils.clean(pair[0]);
    
                if (name == null) {
                    throw new IllegalArgumentException("Filter name not found for filter chain definition token: " + token);
                }
                String config = null;
    
                if (pair.length == 2) {
                    config = StringUtils.clean(pair[1]);
                    //if there was an open bracket, it assumed there is a closing bracket, so strip it too:
                    config = config.substring(0, config.length() - 1);
                    config = StringUtils.clean(config);
    
                    //backwards compatibility prior to implementing SHIRO-205:
                    //prior to SHIRO-205 being implemented, it was common for end-users to quote the config inside brackets
                    //if that config required commas.  We need to strip those quotes to get to the interior quoted definition
                    //to ensure any existing quoted definitions still function for end users:
                    if (config != null && config.startsWith(""") && config.endsWith(""")) {
                        String stripped = config.substring(1, config.length() - 1);
                        stripped = StringUtils.clean(stripped);
    
                        //if the stripped value does not have any internal quotes, we can assume that the entire config was
                        //quoted and we can use the stripped value.
                        if (stripped != null && stripped.indexOf('"') == -1) {
                            config = stripped;
                        }
                        //else:
                        //the remaining config does have internal quotes, so we need to assume that each comma delimited
                        //pair might be quoted, in which case we need the leading and trailing quotes that we stripped
                        //So we ignore the stripped value.
                    }
                }
                
                return new String[]{name, config};
    
            } catch (Exception e) {
                String msg = "Unable to parse filter chain definition token: " + token;
                throw new ConfigurationException(msg, e);
            }
        }
    
        public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {
            if (!StringUtils.hasText(chainName)) {
                throw new IllegalArgumentException("chainName cannot be null or empty.");
            }
            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).");
            }
    
            applyChainConfig(chainName, filter, chainSpecificFilterConfig);
    
            NamedFilterList chain = ensureChain(chainName);
            chain.add(filter);
        }
    
        protected void applyChainConfig(String chainName, Filter filter, String chainSpecificFilterConfig) {
            if (log.isDebugEnabled()) {
                log.debug("Attempting to apply path [" + chainName + "] to filter [" + filter + "] " +
                        "with config [" + chainSpecificFilterConfig + "]");
            }
            if (filter instanceof PathConfigProcessor) {
                ((PathConfigProcessor) filter).processPathConfig(chainName, chainSpecificFilterConfig);
            } else {
                if (StringUtils.hasText(chainSpecificFilterConfig)) {
                    //they specified a filter configuration, but the Filter doesn't implement PathConfigProcessor
                    //this is an erroneous config:
                    String msg = "chainSpecificFilterConfig was specified, but the underlying " +
                            "Filter instance is not an 'instanceof' " +
                            PathConfigProcessor.class.getName() + ".  This is required if the filter is to accept " +
                            "chain-specific configuration.";
                    throw new ConfigurationException(msg);
                }
            }
        }
    
        protected NamedFilterList ensureChain(String chainName) {
            NamedFilterList chain = getChain(chainName);
            if (chain == null) {
                chain = new SimpleNamedFilterList(chainName);
                this.filterChains.put(chainName, chain);
            }
            return chain;
        }
    
        private Map<String, NamedFilterList> filterChains;
    View Code

      解析我们为URL配置的过滤器以及相应的参数是在这一步, 也可以看到其解析以及字符串分割规则。

      这里代码的核心思想就是根据为每个URL建立一个对应的 NamedFilterList, 并将其需要经过的filter维持到NamedFilterList这个属性内部; 然后将这个 NamedFilterList 添加到 org.apache.shiro.web.filter.mgt.DefaultFilterChainManager#filterChains 属性中。

      比如上面的代码解析到的 filterChains如下:

    (3) 创建一个PathMatchingFilterChainResolver 对象, 并将上面(2) 解析到的FilterChainManager 对象作为属性维持到 PathMatchingFilterChainResolver 内部

    (4) 创建一个SpringShiroFilter, 并且构造方法上面传了 WebSecurityManager 和 PathMatchingFilterChainResolver。接下来后期对请求做处理的也就是这两个重要的属性。

      前期的准备以及解析工作已经完成,接下来请求进来就是这个filter 发挥作用。

    【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
  • 相关阅读:
    Show, Attend and Tell: Neural Image Caption Generation with Visual Attention
    (转)Awesome GAN for Medical Imaging
    (转)Awesome Object Detection
    (转)Awesome PyTorch List
    深度学习课程笔记(十七)Meta-learning (Model Agnostic Meta Learning)
    深度学习课程笔记(十六)Recursive Neural Network
    (转)Multi-Object-Tracking-Paper-List
    深度学习课程笔记(十五)Recurrent Neural Network
    (转)Awsome Domain-Adaptation
    论文阅读:Learning Visual Question Answering by Bootstrapping Hard Attention
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/15455543.html
Copyright © 2020-2023  润新知