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.ini.IniFactorySupport;
023import org.apache.shiro.ini.IniSecurityManagerFactory;
024import org.apache.shiro.config.ogdl.ReflectionBuilder;
025import org.apache.shiro.util.CollectionUtils;
026import org.apache.shiro.lang.util.Factory;
027import org.apache.shiro.web.filter.mgt.DefaultFilter;
028import org.apache.shiro.web.filter.mgt.FilterChainManager;
029import org.apache.shiro.web.filter.mgt.FilterChainResolver;
030import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034import javax.servlet.Filter;
035import javax.servlet.FilterConfig;
036import java.util.Collections;
037import java.util.LinkedHashMap;
038import java.util.List;
039import java.util.Map;
040
041/**
042 * A {@link Factory} that creates {@link FilterChainResolver} instances based on {@link Ini} configuration.
043 *
044 * @since 1.0
045 */
046@SuppressWarnings("deprecation")
047public class IniFilterChainResolverFactory extends IniFactorySupport<FilterChainResolver> {
048
049    /**
050     * filters key.
051     */
052    public static final String FILTERS = "filters";
053    /**
054     * urls key.
055     */
056    public static final String URLS = "urls";
057
058    private static final Logger LOGGER = LoggerFactory.getLogger(IniFilterChainResolverFactory.class);
059
060    private FilterConfig filterConfig;
061
062    private List<String> globalFilters = Collections.singletonList(DefaultFilter.invalidRequest.name());
063
064    private boolean caseInsensitive;
065
066    public IniFilterChainResolverFactory() {
067        super();
068    }
069
070    public IniFilterChainResolverFactory(Ini ini) {
071        super(ini);
072    }
073
074    public IniFilterChainResolverFactory(Ini ini, Map<String, ?> defaultBeans) {
075        this(ini);
076        this.setDefaults(defaultBeans);
077    }
078
079    public FilterConfig getFilterConfig() {
080        return filterConfig;
081    }
082
083    public void setFilterConfig(FilterConfig filterConfig) {
084        this.filterConfig = filterConfig;
085    }
086
087    public List<String> getGlobalFilters() {
088        return globalFilters;
089    }
090
091    public void setGlobalFilters(List<String> globalFilters) {
092        this.globalFilters = globalFilters;
093    }
094
095    public boolean isCaseInsensitive() {
096        return caseInsensitive;
097    }
098
099    public void setCaseInsensitive(boolean caseInsensitive) {
100        this.caseInsensitive = caseInsensitive;
101    }
102
103    protected FilterChainResolver createInstance(Ini ini) {
104        FilterChainResolver filterChainResolver = createDefaultInstance();
105        if (filterChainResolver instanceof PathMatchingFilterChainResolver) {
106            PathMatchingFilterChainResolver resolver = (PathMatchingFilterChainResolver) filterChainResolver;
107            FilterChainManager manager = resolver.getFilterChainManager();
108            buildChains(manager, ini);
109        }
110        return filterChainResolver;
111    }
112
113    protected FilterChainResolver createDefaultInstance() {
114        FilterConfig filterConfig = getFilterConfig();
115        if (filterConfig != null) {
116            return new PathMatchingFilterChainResolver(filterConfig).caseInsensitive(caseInsensitive);
117        } else {
118            return new PathMatchingFilterChainResolver().caseInsensitive(caseInsensitive);
119        }
120    }
121
122    protected void buildChains(FilterChainManager manager, Ini ini) {
123        //filters section:
124        Ini.Section section = ini.getSection(FILTERS);
125
126        if (!CollectionUtils.isEmpty(section)) {
127            String msg = "The [{}] section has been deprecated and will be removed in a future release!  Please "
128                    + "move all object configuration (filters and all other objects) to the [{}] section.";
129            LOGGER.warn(msg, FILTERS, IniSecurityManagerFactory.MAIN_SECTION_NAME);
130        }
131
132        Map<String, Object> defaults = new LinkedHashMap<String, Object>();
133
134        Map<String, Filter> defaultFilters = manager.getFilters();
135
136        //now let's see if there are any object defaults in addition to the filters
137        //these can be used to configure the filters:
138        //create a Map of objects to use as the defaults:
139        if (!CollectionUtils.isEmpty(defaultFilters)) {
140            defaults.putAll(defaultFilters);
141        }
142        //User-provided objects must come _after_ the default filters - to allow the user-provided
143        //ones to override the default filters if necessary.
144        Map<String, ?> defaultBeans = getDefaults();
145        if (!CollectionUtils.isEmpty(defaultBeans)) {
146            defaults.putAll(defaultBeans);
147        }
148
149        Map<String, Filter> filters = getFilters(section, defaults);
150
151        //add the filters to the manager:
152        registerFilters(filters, manager);
153
154        manager.setGlobalFilters(getGlobalFilters());
155
156        //urls section:
157        section = ini.getSection(URLS);
158        createChains(section, manager);
159
160        // create the default chain, to match anything the path matching would have missed
161        // TODO this assumes ANT path matching
162        manager.createDefaultChain("/**");
163    }
164
165    protected void registerFilters(Map<String, Filter> filters, FilterChainManager manager) {
166        if (!CollectionUtils.isEmpty(filters)) {
167            //only call filter.init if there is a FilterConfig available
168            boolean init = getFilterConfig() != null;
169            for (Map.Entry<String, Filter> entry : filters.entrySet()) {
170                String name = entry.getKey();
171                Filter filter = entry.getValue();
172                manager.addFilter(name, filter, init);
173            }
174        }
175    }
176
177    protected Map<String, Filter> getFilters(Map<String, String> section, Map<String, ?> defaults) {
178
179        Map<String, Filter> filters = extractFilters(defaults);
180
181        if (!CollectionUtils.isEmpty(section)) {
182            ReflectionBuilder builder = new ReflectionBuilder(defaults);
183            Map<String, ?> built = builder.buildObjects(section);
184            Map<String, Filter> sectionFilters = extractFilters(built);
185
186            if (CollectionUtils.isEmpty(filters)) {
187                filters = sectionFilters;
188            } else {
189                if (!CollectionUtils.isEmpty(sectionFilters)) {
190                    filters.putAll(sectionFilters);
191                }
192            }
193        }
194
195        return filters;
196    }
197
198    private Map<String, Filter> extractFilters(Map<String, ?> objects) {
199        if (CollectionUtils.isEmpty(objects)) {
200            return null;
201        }
202        Map<String, Filter> filterMap = new LinkedHashMap<String, Filter>();
203        for (Map.Entry<String, ?> entry : objects.entrySet()) {
204            String key = entry.getKey();
205            Object value = entry.getValue();
206            if (value instanceof Filter) {
207                filterMap.put(key, (Filter) value);
208            }
209        }
210        return filterMap;
211    }
212
213    protected void createChains(Map<String, String> urls, FilterChainManager manager) {
214        if (CollectionUtils.isEmpty(urls)) {
215            if (LOGGER.isDebugEnabled()) {
216                LOGGER.debug("No urls to process.");
217            }
218            return;
219        }
220
221        if (LOGGER.isTraceEnabled()) {
222            LOGGER.trace("Before url processing.");
223        }
224
225        for (Map.Entry<String, String> entry : urls.entrySet()) {
226            String path = entry.getKey();
227            String value = entry.getValue();
228            manager.createChain(path, value);
229        }
230    }
231}