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 */
019package org.apache.shiro.web.config;
020
021import org.apache.shiro.config.Ini;
022import org.apache.shiro.config.IniFactorySupport;
023import org.apache.shiro.config.IniSecurityManagerFactory;
024import org.apache.shiro.config.ReflectionBuilder;
025import org.apache.shiro.util.CollectionUtils;
026import org.apache.shiro.util.Factory;
027import org.apache.shiro.web.filter.mgt.FilterChainManager;
028import org.apache.shiro.web.filter.mgt.FilterChainResolver;
029import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033import javax.servlet.Filter;
034import javax.servlet.FilterConfig;
035import java.util.LinkedHashMap;
036import 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 */
043public 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}