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}