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