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}