001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    package org.apache.shiro.web.config;
020    
021    import org.apache.shiro.config.Ini;
022    import org.apache.shiro.config.IniFactorySupport;
023    import org.apache.shiro.config.IniSecurityManagerFactory;
024    import org.apache.shiro.config.ReflectionBuilder;
025    import org.apache.shiro.util.CollectionUtils;
026    import org.apache.shiro.util.Factory;
027    import org.apache.shiro.web.filter.mgt.FilterChainManager;
028    import org.apache.shiro.web.filter.mgt.FilterChainResolver;
029    import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
030    import org.slf4j.Logger;
031    import org.slf4j.LoggerFactory;
032    
033    import javax.servlet.Filter;
034    import javax.servlet.FilterConfig;
035    import java.util.LinkedHashMap;
036    import java.util.Map;
037    
038    /**
039     * A {@link Factory} that creates {@link FilterChainResolver} instances based on {@link Ini} configuration.
040     *
041     * @since 1.0
042     */
043    public class IniFilterChainResolverFactory extends IniFactorySupport<FilterChainResolver> {
044    
045        public static final String FILTERS = "filters";
046        public static final String URLS = "urls";
047    
048        private static transient final Logger log = LoggerFactory.getLogger(IniFilterChainResolverFactory.class);
049    
050        private FilterConfig filterConfig;
051    
052        private Map<String, ?> defaultBeans;
053    
054        public IniFilterChainResolverFactory() {
055            super();
056        }
057    
058        public IniFilterChainResolverFactory(Ini ini) {
059            super(ini);
060        }
061    
062        public IniFilterChainResolverFactory(Ini ini, Map<String, ?> defaultBeans) {
063            this(ini);
064            this.defaultBeans = defaultBeans;
065        }
066    
067        public FilterConfig getFilterConfig() {
068            return filterConfig;
069        }
070    
071        public void setFilterConfig(FilterConfig filterConfig) {
072            this.filterConfig = filterConfig;
073        }
074    
075        protected FilterChainResolver createInstance(Ini ini) {
076            FilterChainResolver filterChainResolver = createDefaultInstance();
077            if (filterChainResolver instanceof PathMatchingFilterChainResolver) {
078                PathMatchingFilterChainResolver resolver = (PathMatchingFilterChainResolver) filterChainResolver;
079                FilterChainManager manager = resolver.getFilterChainManager();
080                buildChains(manager, ini);
081            }
082            return filterChainResolver;
083        }
084    
085        protected FilterChainResolver createDefaultInstance() {
086            FilterConfig filterConfig = getFilterConfig();
087            if (filterConfig != null) {
088                return new PathMatchingFilterChainResolver(filterConfig);
089            } else {
090                return new PathMatchingFilterChainResolver();
091            }
092        }
093    
094        protected void buildChains(FilterChainManager manager, Ini ini) {
095            //filters section:
096            Ini.Section section = ini.getSection(FILTERS);
097    
098            if (!CollectionUtils.isEmpty(section)) {
099                String msg = "The [{}] section has been deprecated and will be removed in a future release!  Please " +
100                        "move all object configuration (filters and all other objects) to the [{}] section.";
101                log.warn(msg, FILTERS, IniSecurityManagerFactory.MAIN_SECTION_NAME);
102            }
103    
104            Map<String, Object> defaults = new LinkedHashMap<String, Object>();
105    
106            Map<String, Filter> defaultFilters = manager.getFilters();
107    
108            //now let's see if there are any object defaults in addition to the filters
109            //these can be used to configure the filters:
110            //create a Map of objects to use as the defaults:
111            if (!CollectionUtils.isEmpty(defaultFilters)) {
112                defaults.putAll(defaultFilters);
113            }
114            //User-provided objects must come _after_ the default filters - to allow the user-provided
115            //ones to override the default filters if necessary.
116            if (!CollectionUtils.isEmpty(this.defaultBeans)) {
117                defaults.putAll(this.defaultBeans);
118            }
119    
120            Map<String, Filter> filters = getFilters(section, defaults);
121    
122            //add the filters to the manager:
123            registerFilters(filters, manager);
124    
125            //urls section:
126            section = ini.getSection(URLS);
127            createChains(section, manager);
128        }
129    
130        protected void registerFilters(Map<String, Filter> filters, FilterChainManager manager) {
131            if (!CollectionUtils.isEmpty(filters)) {
132                boolean init = getFilterConfig() != null; //only call filter.init if there is a FilterConfig available
133                for (Map.Entry<String, Filter> entry : filters.entrySet()) {
134                    String name = entry.getKey();
135                    Filter filter = entry.getValue();
136                    manager.addFilter(name, filter, init);
137                }
138            }
139        }
140    
141        protected Map<String, Filter> getFilters(Map<String, String> section, Map<String, ?> defaults) {
142    
143            Map<String, Filter> filters = extractFilters(defaults);
144    
145            if (!CollectionUtils.isEmpty(section)) {
146                ReflectionBuilder builder = new ReflectionBuilder(defaults);
147                Map<String, ?> built = builder.buildObjects(section);
148                Map<String,Filter> sectionFilters = extractFilters(built);
149    
150                if (CollectionUtils.isEmpty(filters)) {
151                    filters = sectionFilters;
152                } else {
153                    if (!CollectionUtils.isEmpty(sectionFilters)) {
154                        filters.putAll(sectionFilters);
155                    }
156                }
157            }
158    
159            return filters;
160        }
161    
162        private Map<String, Filter> extractFilters(Map<String, ?> objects) {
163            if (CollectionUtils.isEmpty(objects)) {
164                return null;
165            }
166            Map<String, Filter> filterMap = new LinkedHashMap<String, Filter>();
167            for (Map.Entry<String, ?> entry : objects.entrySet()) {
168                String key = entry.getKey();
169                Object value = entry.getValue();
170                if (value instanceof Filter) {
171                    filterMap.put(key, (Filter) value);
172                }
173            }
174            return filterMap;
175        }
176    
177        protected void createChains(Map<String, String> urls, FilterChainManager manager) {
178            if (CollectionUtils.isEmpty(urls)) {
179                if (log.isDebugEnabled()) {
180                    log.debug("No urls to process.");
181                }
182                return;
183            }
184    
185            if (log.isTraceEnabled()) {
186                log.trace("Before url processing.");
187            }
188    
189            for (Map.Entry<String, String> entry : urls.entrySet()) {
190                String path = entry.getKey();
191                String value = entry.getValue();
192                manager.createChain(path, value);
193            }
194        }
195    }