1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.apache.shiro.web.servlet; 20 21 import org.apache.shiro.config.ConfigurationException; 22 import org.apache.shiro.config.Ini; 23 import org.apache.shiro.config.IniFactorySupport; 24 import org.apache.shiro.io.ResourceUtils; 25 import org.apache.shiro.mgt.SecurityManager; 26 import org.apache.shiro.util.CollectionUtils; 27 import org.apache.shiro.util.StringUtils; 28 import org.apache.shiro.web.config.IniFilterChainResolverFactory; 29 import org.apache.shiro.web.config.WebIniSecurityManagerFactory; 30 import org.apache.shiro.web.filter.mgt.FilterChainResolver; 31 import org.apache.shiro.web.mgt.WebSecurityManager; 32 import org.apache.shiro.web.util.WebUtils; 33 import org.slf4j.Logger; 34 import org.slf4j.LoggerFactory; 35 36 import java.io.InputStream; 37 import java.util.Map; 38 39 /** 40 * <h1>Deprecated</h1> 41 * This filter has been deprecated as of Shiro 1.2 in favor of using the {@link ShiroFilter} in {@code web.xml} instead. 42 * See the {@link ShiroFilter} JavaDoc for usage. 43 * <p/> 44 * ====================== 45 * <p/> 46 * Servlet Filter that configures and enables all Shiro functions within a web application by using the 47 * <a href="http://en.wikipedia.org/wiki/INI_file">INI</a> configuration format. 48 * <p/> 49 * The actual INI configuration contents are not covered here, but instead in Shiro's 50 * <a href="http://shiro.apache.org/configuration.html">Configuration Documentation</a> and additional web-specific 51 * <a href="http://shiro.apache.org/web.html">Web Documentation</a>. 52 * <h2>Usage</h2> 53 * <h3>Default</h3> 54 * By default, the simplest filter declaration expects a {@code shiro.ini} resource to be located at 55 * {@code /WEB-INF/shiro.ini}, or, if not there, falls back to checking the root of the classpath 56 * (i.e. {@code classpath:shiro.ini}): 57 * <pre> 58 * <filter> 59 * <filter-name>ShiroFilter</filter-name> 60 * <filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class> 61 * </filter> 62 * </pre> 63 * <h3>Custom Path</h3> 64 * If you want the INI configuration to be somewhere other than {@code /WEB-INF/shiro.ini} or 65 * {@code classpath:shiro.ini}, you may specify an alternate location via the {@code configPath init-param}: 66 * <pre> 67 * <filter> 68 * <filter-name>ShiroFilter</filter-name> 69 * <filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class> 70 * <init-param> 71 * <param-name>configPath</param-name> 72 * <param-value>/WEB-INF/someFile.ini</param-value> 73 * </init-param> 74 * </filter> 75 * </pre> 76 * Unqualified (schemeless or 'non-prefixed') paths are assumed to be {@code ServletContext} resource paths, resolvable 77 * via {@link javax.servlet.ServletContext#getResourceAsStream(String) ServletContext#getResourceAsStream}. 78 * <p/> 79 * Non-ServletContext resources may be loaded from qualified locations by specifying prefixes indicating the source, 80 * e.g. {@code file:}, {@code url:}, and {@code classpath:}. See the 81 * {@link ResourceUtils#getInputStreamForPath(String)} JavaDoc for more. 82 * <h3>Inline</h3> 83 * For relatively simple environments, you can embed the INI config directly inside the filter declaration with 84 * the {@code config init-param}: 85 * <pre> 86 * <filter> 87 * <filter-name>ShiroFilter</filter-name> 88 * <filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class> 89 * <init-param> 90 * <param-name>config</param-name> 91 * <param-value> 92 * #INI config goes here... 93 * </param-value> 94 * </init-param> 95 * </filter> 96 * </pre> 97 * Although this is typically not recommended because any Shiro configuration changes would contribute to version control 98 * 'noise' in the web.xml file. 99 * <p/> 100 * When creating the shiro.ini configuration itself, please see Shiro's 101 * <a href="http://shiro.apache.org/configuration.html">Configuration Documentation</a> and 102 * <a href="http://shiro.apache.org/web.html">Web Documentation</a>. 103 * 104 * @see <a href="http://shiro.apache.org/configuration.html">Apache Shiro INI Configuration</a> 105 * @see <a href="http://shiro.apache.org/web.html">Apache Shiro Web Documentation</a> 106 * @since 1.0 107 * @deprecated in 1.2 in favor of using the {@link ShiroFilter} 108 */ 109 @Deprecated 110 public class IniShiroFilter extends AbstractShiroFilter { 111 112 public static final String CONFIG_INIT_PARAM_NAME = "config"; 113 public static final String CONFIG_PATH_INIT_PARAM_NAME = "configPath"; 114 115 public static final String DEFAULT_WEB_INI_RESOURCE_PATH = "/WEB-INF/shiro.ini"; 116 117 private static final Logger log = LoggerFactory.getLogger(IniShiroFilter.class); 118 119 private String config; 120 private String configPath; 121 122 public IniShiroFilter() { 123 } 124 125 /** 126 * Returns the actual INI configuration text to use to build the {@link SecurityManager} and 127 * {@link FilterChainResolver} used by the web application or {@code null} if the 128 * {@link #getConfigPath() configPath} should be used to load a fallback INI source. 129 * <p/> 130 * This value is {@code null} by default, but it will be automatically set to the value of the 131 * '{@code config}' {@code init-param} if it exists in the {@code FilterConfig} provided by the servlet 132 * container at startup. 133 * 134 * @return the actual INI configuration text to use to build the {@link SecurityManager} and 135 * {@link FilterChainResolver} used by the web application or {@code null} if the 136 * {@link #getConfigPath() configPath} should be used to load a fallback INI source. 137 */ 138 public String getConfig() { 139 return this.config; 140 } 141 142 /** 143 * Sets the actual INI configuration text to use to build the {@link SecurityManager} and 144 * {@link FilterChainResolver} used by the web application. If this value is {@code null}, the 145 * {@link #getConfigPath() configPath} will be checked to see if a .ini file should be loaded instead. 146 * <p/> 147 * This value is {@code null} by default, but it will be automatically set to the value of the 148 * '{@code config}' {@code init-param} if it exists in the {@code FilterConfig} provided by the servlet 149 * container at startup. 150 * 151 * @param config the actual INI configuration text to use to build the {@link SecurityManager} and 152 * {@link FilterChainResolver} used by the web application. 153 */ 154 public void setConfig(String config) { 155 this.config = config; 156 } 157 158 /** 159 * Returns the config path to be used to load a .ini file for configuration if a configuration is 160 * not specified via the {@link #getConfig() config} attribute. 161 * <p/> 162 * This value is {@code null} by default, but it will be automatically set to the value of the 163 * '{@code configPath}' {@code init-param} if it exists in the {@code FilterConfig} provided by the servlet 164 * container at startup. 165 * 166 * @return the config path to be used to load a .ini file for configuration if a configuration is 167 * not specified via the {@link #getConfig() config} attribute. 168 */ 169 public String getConfigPath() { 170 return configPath; 171 } 172 173 /** 174 * Sets the config path to be used to load a .ini file for configuration if a configuration is 175 * not specified via the {@link #getConfig() config} attribute. 176 * <p/> 177 * This value is {@code null} by default, but it will be automatically set to the value of the 178 * '{@code configPath}' {@code init-param} if it exists in the {@code FilterConfig} provided by the servlet 179 * container at startup. 180 * 181 * @param configPath the config path to be used to load a .ini file for configuration if a configuration is 182 * not specified via the {@link #getConfig() config} attribute. 183 */ 184 public void setConfigPath(String configPath) { 185 this.configPath = StringUtils.clean(configPath); 186 } 187 188 public void init() throws Exception { 189 applyInitParams(); 190 configure(); 191 } 192 193 protected void applyInitParams() throws Exception { 194 String config = getInitParam(CONFIG_INIT_PARAM_NAME); 195 if (config != null) { 196 setConfig(config); 197 } 198 String configPath = getInitParam(CONFIG_PATH_INIT_PARAM_NAME); 199 if (configPath != null) { 200 setConfigPath(configPath); 201 } 202 } 203 204 protected void configure() throws Exception { 205 Ini ini = loadIniFromConfig(); 206 207 if (CollectionUtils.isEmpty(ini)) { 208 log.info("Null or empty configuration specified via 'config' init-param. " + 209 "Checking path-based configuration."); 210 ini = loadIniFromPath(); 211 } 212 //added for SHIRO-178: 213 if (CollectionUtils.isEmpty(ini)) { 214 log.info("Null or empty configuration specified via '" + CONFIG_INIT_PARAM_NAME + "' or '" + 215 CONFIG_PATH_INIT_PARAM_NAME + "' filter parameters. Trying the default " + 216 DEFAULT_WEB_INI_RESOURCE_PATH + " file."); 217 ini = getServletContextIniResource(DEFAULT_WEB_INI_RESOURCE_PATH); 218 } 219 //although the preferred default is /WEB-INF/shiro.ini per SHIRO-178, keep this 220 //for backwards compatibility: 221 if (CollectionUtils.isEmpty(ini)) { 222 log.info("Null or empty configuration specified via '" + CONFIG_INIT_PARAM_NAME + "' or '" + 223 CONFIG_PATH_INIT_PARAM_NAME + "' filter parameters. Trying the default " + 224 IniFactorySupport.DEFAULT_INI_RESOURCE_PATH + " file."); 225 ini = IniFactorySupport.loadDefaultClassPathIni(); 226 } 227 228 Map<String, ?> objects = applySecurityManager(ini); 229 applyFilterChainResolver(ini, objects); 230 } 231 232 protected Ini loadIniFromConfig() { 233 Ini ini = null; 234 String config = getConfig(); 235 if (config != null) { 236 ini = convertConfigToIni(config); 237 } 238 return ini; 239 } 240 241 protected Ini loadIniFromPath() { 242 Ini ini = null; 243 String path = getConfigPath(); 244 if (path != null) { 245 ini = convertPathToIni(path); 246 } 247 return ini; 248 } 249 250 protected Map<String, ?> applySecurityManager(Ini ini) { 251 WebIniSecurityManagerFactory factory; 252 if (CollectionUtils.isEmpty(ini)) { 253 factory = new WebIniSecurityManagerFactory(); 254 } else { 255 factory = new WebIniSecurityManagerFactory(ini); 256 } 257 258 // Create the security manager and check that it implements WebSecurityManager. 259 // Otherwise, it can't be used with the filter. 260 SecurityManager securityManager = factory.getInstance(); 261 if (!(securityManager instanceof WebSecurityManager)) { 262 String msg = "The configured security manager is not an instance of WebSecurityManager, so " + 263 "it can not be used with the Shiro servlet filter."; 264 throw new ConfigurationException(msg); 265 } 266 267 setSecurityManager((WebSecurityManager) securityManager); 268 269 return factory.getBeans(); 270 } 271 272 protected void applyFilterChainResolver(Ini ini, Map<String, ?> defaults) { 273 if (ini == null || ini.isEmpty()) { 274 //nothing to use to create the resolver, just return 275 //(the AbstractShiroFilter allows a null resolver, in which case the original FilterChain is 276 // always used). 277 return; 278 } 279 280 //only create a resolver if the 'filters' or 'urls' sections are defined: 281 Ini.Section urls = ini.getSection(IniFilterChainResolverFactory.URLS); 282 Ini.Section filters = ini.getSection(IniFilterChainResolverFactory.FILTERS); 283 if ((urls != null && !urls.isEmpty()) || (filters != null && !filters.isEmpty())) { 284 //either the urls section or the filters section was defined. Go ahead and create the resolver 285 //and set it: 286 IniFilterChainResolverFactory filterChainResolverFactory = new IniFilterChainResolverFactory(ini, defaults); 287 filterChainResolverFactory.setFilterConfig(getFilterConfig()); 288 FilterChainResolver resolver = filterChainResolverFactory.getInstance(); 289 setFilterChainResolver(resolver); 290 } 291 } 292 293 protected Ini convertConfigToIni(String config) { 294 Ini ini = new Ini(); 295 ini.load(config); 296 return ini; 297 } 298 299 /** 300 * Returns the INI instance reflecting the specified servlet context resource path or {@code null} if no 301 * resource was found. 302 * 303 * @param servletContextPath the servlet context resource path of the INI file to load 304 * @return the INI instance reflecting the specified servlet context resource path or {@code null} if no 305 * resource was found. 306 * @since 1.2 307 */ 308 protected Ini getServletContextIniResource(String servletContextPath) { 309 String path = WebUtils.normalize(servletContextPath); 310 if (getServletContext() != null) { 311 InputStream is = getServletContext().getResourceAsStream(path); 312 if (is != null) { 313 Ini ini = new Ini(); 314 ini.load(is); 315 if (CollectionUtils.isEmpty(ini)) { 316 log.warn("ServletContext INI resource '" + servletContextPath + "' exists, but it did not contain " + 317 "any data."); 318 } 319 return ini; 320 } 321 } 322 return null; 323 } 324 325 /** 326 * Converts the specified file path to an {@link Ini} instance. 327 * <p/> 328 * If the path does not have a resource prefix as defined by {@link ResourceUtils#hasResourcePrefix(String)}, the 329 * path is expected to be resolvable by the {@code ServletContext} via 330 * {@link javax.servlet.ServletContext#getResourceAsStream(String)}. 331 * 332 * @param path the path of the INI resource to load into an INI instance. 333 * @return an INI instance populated based on the given INI resource path. 334 */ 335 protected Ini convertPathToIni(String path) { 336 337 Ini ini = new Ini(); 338 339 //SHIRO-178: Check for servlet context resource and not 340 //only resource paths: 341 if (!ResourceUtils.hasResourcePrefix(path)) { 342 ini = getServletContextIniResource(path); 343 if (ini == null) { 344 String msg = "There is no servlet context resource corresponding to configPath '" + path + "' If " + 345 "the resource is located elsewhere (not immediately resolveable in the servlet context), " + 346 "specify an appropriate classpath:, url:, or file: resource prefix accordingly."; 347 throw new ConfigurationException(msg); 348 } 349 } else { 350 //normal resource path - load as usual: 351 ini.loadFromPath(path); 352 } 353 354 return ini; 355 } 356 }