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