View Javadoc
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   * &lt;filter&gt;
59   *     &lt;filter-name&gt;ShiroFilter&lt;/filter-name&gt;
60   *     &lt;filter-class&gt;org.apache.shiro.web.servlet.IniShiroFilter&lt;/filter-class&gt;
61   * &lt;/filter&gt;
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   * &lt;filter&gt;
68   *     &lt;filter-name&gt;ShiroFilter&lt;/filter-name&gt;
69   *     &lt;filter-class&gt;org.apache.shiro.web.servlet.IniShiroFilter&lt;/filter-class&gt;
70   *     &lt;init-param&gt;
71   *         &lt;param-name&gt;configPath&lt;/param-name&gt;
72   *         &lt;param-value&gt;/WEB-INF/someFile.ini&lt;/param-value&gt;
73   *     &lt;/init-param&gt;
74   * &lt;/filter&gt;
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   * &lt;filter&gt;
87   *     &lt;filter-name&gt;ShiroFilter&lt;/filter-name&gt;
88   *     &lt;filter-class&gt;org.apache.shiro.web.servlet.IniShiroFilter&lt;/filter-class&gt;
89   *     &lt;init-param&gt;
90   *         &lt;param-name&gt;config&lt;/param-name&gt;
91   *         &lt;param-value&gt;
92   *             #INI config goes here...
93   *      &lt;/param-value&gt;
94   *     &lt;/init-param&gt;
95   * &lt;/filter&gt;
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             IniFilterChainResolverFactorylterChainResolverFactory">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 }