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.servlet; 020 021import org.apache.shiro.config.ConfigurationException; 022import org.apache.shiro.config.Ini; 023import org.apache.shiro.ini.IniFactorySupport; 024import org.apache.shiro.lang.io.ResourceUtils; 025import org.apache.shiro.mgt.SecurityManager; 026import org.apache.shiro.util.CollectionUtils; 027import org.apache.shiro.lang.util.StringUtils; 028import org.apache.shiro.web.config.IniFilterChainResolverFactory; 029import org.apache.shiro.web.config.WebIniSecurityManagerFactory; 030import org.apache.shiro.web.filter.mgt.FilterChainResolver; 031import org.apache.shiro.web.mgt.WebSecurityManager; 032import org.apache.shiro.web.util.WebUtils; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036import java.io.InputStream; 037import java.util.Map; 038 039/** 040 * <h1>Deprecated</h1> 041 * This filter has been deprecated as of Shiro 1.2 in favor of using the {@link ShiroFilter} in {@code web.xml} instead. 042 * See the {@link ShiroFilter} JavaDoc for usage. 043 * <p/> 044 * ====================== 045 * <p/> 046 * Servlet Filter that configures and enables all Shiro functions within a web application by using the 047 * <a href="http://en.wikipedia.org/wiki/INI_file">INI</a> configuration format. 048 * <p/> 049 * The actual INI configuration contents are not covered here, but instead in Shiro's 050 * <a href="https://shiro.apache.org/configuration.html">Configuration Documentation</a> and additional web-specific 051 * <a href="https://shiro.apache.org/web.html">Web Documentation</a>. 052 * <h2>Usage</h2> 053 * <h3>Default</h3> 054 * By default, the simplest filter declaration expects a {@code shiro.ini} resource to be located at 055 * {@code /WEB-INF/shiro.ini}, or, if not there, falls back to checking the root of the classpath 056 * (i.e. {@code classpath:shiro.ini}): 057 * <pre> 058 * <filter> 059 * <filter-name>ShiroFilter</filter-name> 060 * <filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class> 061 * </filter> 062 * </pre> 063 * <h3>Custom Path</h3> 064 * If you want the INI configuration to be somewhere other than {@code /WEB-INF/shiro.ini} or 065 * {@code classpath:shiro.ini}, you may specify an alternate location via the {@code configPath init-param}: 066 * <pre> 067 * <filter> 068 * <filter-name>ShiroFilter</filter-name> 069 * <filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class> 070 * <init-param> 071 * <param-name>configPath</param-name> 072 * <param-value>/WEB-INF/someFile.ini</param-value> 073 * </init-param> 074 * </filter> 075 * </pre> 076 * Unqualified (schemeless or 'non-prefixed') paths are assumed to be {@code ServletContext} resource paths, resolvable 077 * via {@link javax.servlet.ServletContext#getResourceAsStream(String) ServletContext#getResourceAsStream}. 078 * <p/> 079 * Non-ServletContext resources may be loaded from qualified locations by specifying prefixes indicating the source, 080 * e.g. {@code file:}, {@code url:}, and {@code classpath:}. See the 081 * {@link ResourceUtils#getInputStreamForPath(String)} JavaDoc for more. 082 * <h3>Inline</h3> 083 * For relatively simple environments, you can embed the INI config directly inside the filter declaration with 084 * the {@code config init-param}: 085 * <pre> 086 * <filter> 087 * <filter-name>ShiroFilter</filter-name> 088 * <filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class> 089 * <init-param> 090 * <param-name>config</param-name> 091 * <param-value> 092 * #INI config goes here... 093 * </param-value> 094 * </init-param> 095 * </filter> 096 * </pre> 097 * Although this is typically not recommended because any Shiro configuration changes would contribute to version control 098 * 'noise' in the web.xml file. 099 * <p/> 100 * When creating the shiro.ini configuration itself, please see Shiro's 101 * <a href="https://shiro.apache.org/configuration.html">Configuration Documentation</a> and 102 * <a href="https://shiro.apache.org/web.html">Web Documentation</a>. 103 * 104 * @see <a href="https://shiro.apache.org/configuration.html">Apache Shiro INI Configuration</a> 105 * @see <a href="https://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 110public class IniShiroFilter extends AbstractShiroFilter { 111 112 /** 113 * ini config param name. 114 */ 115 public static final String CONFIG_INI_PARAM_NAME = "config"; 116 117 /** 118 * ini config path param name. 119 */ 120 public static final String CONFIG_PATH_INI_PARAM_NAME = "configPath"; 121 122 /** 123 * default web ini resource path. 124 */ 125 public static final String DEFAULT_WEB_INI_RESOURCE_PATH = "/WEB-INF/shiro.ini"; 126 127 private static final Logger LOGGER = LoggerFactory.getLogger(IniShiroFilter.class); 128 129 private String config; 130 private String configPath; 131 132 public IniShiroFilter() { 133 } 134 135 /** 136 * Returns the actual INI configuration text to use to build the {@link SecurityManager} and 137 * {@link FilterChainResolver} used by the web application or {@code null} if the 138 * {@link #getConfigPath() configPath} should be used to load a fallback INI source. 139 * <p/> 140 * This value is {@code null} by default, but it will be automatically set to the value of the 141 * '{@code config}' {@code init-param} if it exists in the {@code FilterConfig} provided by the servlet 142 * container at startup. 143 * 144 * @return the actual INI configuration text to use to build the {@link SecurityManager} and 145 * {@link FilterChainResolver} used by the web application or {@code null} if the 146 * {@link #getConfigPath() configPath} should be used to load a fallback INI source. 147 */ 148 public String getConfig() { 149 return this.config; 150 } 151 152 /** 153 * Sets the actual INI configuration text to use to build the {@link SecurityManager} and 154 * {@link FilterChainResolver} used by the web application. If this value is {@code null}, the 155 * {@link #getConfigPath() configPath} will be checked to see if a .ini file should be loaded instead. 156 * <p/> 157 * This value is {@code null} by default, but it will be automatically set to the value of the 158 * '{@code config}' {@code init-param} if it exists in the {@code FilterConfig} provided by the servlet 159 * container at startup. 160 * 161 * @param config the actual INI configuration text to use to build the {@link SecurityManager} and 162 * {@link FilterChainResolver} used by the web application. 163 */ 164 public void setConfig(String config) { 165 this.config = config; 166 } 167 168 /** 169 * Returns the config path to be used to load a .ini file for configuration if a configuration is 170 * not specified via the {@link #getConfig() config} attribute. 171 * <p/> 172 * This value is {@code null} by default, but it will be automatically set to the value of the 173 * '{@code configPath}' {@code init-param} if it exists in the {@code FilterConfig} provided by the servlet 174 * container at startup. 175 * 176 * @return the config path to be used to load a .ini file for configuration if a configuration is 177 * not specified via the {@link #getConfig() config} attribute. 178 */ 179 public String getConfigPath() { 180 return configPath; 181 } 182 183 /** 184 * Sets the config path to be used to load a .ini file for configuration if a configuration is 185 * not specified via the {@link #getConfig() config} attribute. 186 * <p/> 187 * This value is {@code null} by default, but it will be automatically set to the value of the 188 * '{@code configPath}' {@code init-param} if it exists in the {@code FilterConfig} provided by the servlet 189 * container at startup. 190 * 191 * @param configPath the config path to be used to load a .ini file for configuration if a configuration is 192 * not specified via the {@link #getConfig() config} attribute. 193 */ 194 public void setConfigPath(String configPath) { 195 this.configPath = StringUtils.clean(configPath); 196 } 197 198 public void init() throws Exception { 199 applyInitParams(); 200 configure(); 201 } 202 203 protected void applyInitParams() throws Exception { 204 String config = getInitParam(CONFIG_INI_PARAM_NAME); 205 if (config != null) { 206 setConfig(config); 207 } 208 String configPath = getInitParam(CONFIG_PATH_INI_PARAM_NAME); 209 if (configPath != null) { 210 setConfigPath(configPath); 211 } 212 } 213 214 protected void configure() throws Exception { 215 Ini ini = loadIniFromConfig(); 216 217 if (CollectionUtils.isEmpty(ini)) { 218 LOGGER.info("Null or empty configuration specified via 'config' init-param. " 219 + "Checking path-based configuration."); 220 ini = loadIniFromPath(); 221 } 222 //added for SHIRO-178: 223 if (CollectionUtils.isEmpty(ini)) { 224 LOGGER.info("Null or empty configuration specified via '" + CONFIG_INI_PARAM_NAME + "' or '" 225 + CONFIG_PATH_INI_PARAM_NAME + "' filter parameters. Trying the default " 226 + DEFAULT_WEB_INI_RESOURCE_PATH + " file."); 227 ini = getServletContextIniResource(DEFAULT_WEB_INI_RESOURCE_PATH); 228 } 229 //although the preferred default is /WEB-INF/shiro.ini per SHIRO-178, keep this 230 //for backwards compatibility: 231 if (CollectionUtils.isEmpty(ini)) { 232 LOGGER.info("Null or empty configuration specified via '" + CONFIG_INI_PARAM_NAME + "' or '" 233 + CONFIG_PATH_INI_PARAM_NAME + "' filter parameters. Trying the default " 234 + IniFactorySupport.DEFAULT_INI_RESOURCE_PATH + " file."); 235 ini = IniFactorySupport.loadDefaultClassPathIni(); 236 } 237 238 Map<String, ?> objects = applySecurityManager(ini); 239 applyFilterChainResolver(ini, objects); 240 } 241 242 protected Ini loadIniFromConfig() { 243 Ini ini = null; 244 String config = getConfig(); 245 if (config != null) { 246 ini = convertConfigToIni(config); 247 } 248 return ini; 249 } 250 251 protected Ini loadIniFromPath() { 252 Ini ini = null; 253 String path = getConfigPath(); 254 if (path != null) { 255 ini = convertPathToIni(path); 256 } 257 return ini; 258 } 259 260 protected Map<String, ?> applySecurityManager(Ini ini) { 261 WebIniSecurityManagerFactory factory; 262 if (CollectionUtils.isEmpty(ini)) { 263 factory = new WebIniSecurityManagerFactory(); 264 } else { 265 factory = new WebIniSecurityManagerFactory(ini); 266 } 267 268 // Create the security manager and check that it implements WebSecurityManager. 269 // Otherwise, it can't be used with the filter. 270 SecurityManager securityManager = factory.getInstance(); 271 if (!(securityManager instanceof WebSecurityManager)) { 272 String msg = "The configured security manager is not an instance of WebSecurityManager, so " 273 + "it can not be used with the Shiro servlet filter."; 274 throw new ConfigurationException(msg); 275 } 276 277 setSecurityManager((WebSecurityManager) securityManager); 278 279 return factory.getBeans(); 280 } 281 282 protected void applyFilterChainResolver(Ini ini, Map<String, ?> defaults) { 283 if (ini == null || ini.isEmpty()) { 284 //nothing to use to create the resolver, just return 285 //(the AbstractShiroFilter allows a null resolver, in which case the original FilterChain is 286 // always used). 287 return; 288 } 289 290 //only create a resolver if the 'filters' or 'urls' sections are defined: 291 Ini.Section urls = ini.getSection(IniFilterChainResolverFactory.URLS); 292 Ini.Section filters = ini.getSection(IniFilterChainResolverFactory.FILTERS); 293 if ((urls != null && !urls.isEmpty()) || (filters != null && !filters.isEmpty())) { 294 //either the urls section or the filters section was defined. Go ahead and create the resolver 295 //and set it: 296 IniFilterChainResolverFactory filterChainResolverFactory = new IniFilterChainResolverFactory(ini, defaults); 297 filterChainResolverFactory.setFilterConfig(getFilterConfig()); 298 FilterChainResolver resolver = filterChainResolverFactory.getInstance(); 299 setFilterChainResolver(resolver); 300 } 301 } 302 303 protected Ini convertConfigToIni(String config) { 304 Ini ini = new Ini(); 305 ini.load(config); 306 return ini; 307 } 308 309 /** 310 * Returns the INI instance reflecting the specified servlet context resource path or {@code null} if no 311 * resource was found. 312 * 313 * @param servletContextPath the servlet context resource path of the INI file to load 314 * @return the INI instance reflecting the specified servlet context resource path or {@code null} if no 315 * resource was found. 316 * @since 1.2 317 */ 318 protected Ini getServletContextIniResource(String servletContextPath) { 319 String path = WebUtils.normalize(servletContextPath); 320 if (getServletContext() != null) { 321 InputStream is = getServletContext().getResourceAsStream(path); 322 if (is != null) { 323 Ini ini = new Ini(); 324 ini.load(is); 325 if (CollectionUtils.isEmpty(ini)) { 326 LOGGER.warn("ServletContext INI resource '" + servletContextPath + "' exists, but it did not contain " 327 + "any data."); 328 } 329 return ini; 330 } 331 } 332 return null; 333 } 334 335 /** 336 * Converts the specified file path to an {@link Ini} instance. 337 * <p/> 338 * If the path does not have a resource prefix as defined by {@link ResourceUtils#hasResourcePrefix(String)}, the 339 * path is expected to be resolvable by the {@code ServletContext} via 340 * {@link javax.servlet.ServletContext#getResourceAsStream(String)}. 341 * 342 * @param path the path of the INI resource to load into an INI instance. 343 * @return an INI instance populated based on the given INI resource path. 344 */ 345 protected Ini convertPathToIni(String path) { 346 347 Ini ini = new Ini(); 348 349 //SHIRO-178: Check for servlet context resource and not 350 //only resource paths: 351 if (!ResourceUtils.hasResourcePrefix(path)) { 352 ini = getServletContextIniResource(path); 353 if (ini == null) { 354 String msg = "There is no servlet context resource corresponding to configPath '" + path + "' If " 355 + "the resource is located elsewhere (not immediately resolvable in the servlet context), " 356 + "specify an appropriate classpath:, url:, or file: resource prefix accordingly."; 357 throw new ConfigurationException(msg); 358 } 359 } else { 360 //normal resource path - load as usual: 361 ini.loadFromPath(path); 362 } 363 364 return ini; 365 } 366}