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.filter;
20  
21  import org.apache.shiro.SecurityUtils;
22  import org.apache.shiro.subject.Subject;
23  import org.apache.shiro.web.util.WebUtils;
24  
25  import javax.servlet.ServletRequest;
26  import javax.servlet.ServletResponse;
27  import java.io.IOException;
28  
29  /**
30   * Superclass for any filter that controls access to a resource and may redirect the user to the login page
31   * if they are not authenticated.  This superclass provides the method
32   * {@link #saveRequestAndRedirectToLogin(javax.servlet.ServletRequest, javax.servlet.ServletResponse)}
33   * which is used by many subclasses as the behavior when a user is unauthenticated.
34   *
35   * @since 0.9
36   */
37  public abstract class AccessControlFilter extends PathMatchingFilter {
38  
39      /**
40       * Simple default login URL equal to <code>/login.jsp</code>, which can be overridden by calling the
41       * {@link #setLoginUrl(String) setLoginUrl} method.
42       */
43      public static final String DEFAULT_LOGIN_URL = "/login.jsp";
44  
45      /**
46       * Constant representing the HTTP 'GET' request method, equal to <code>GET</code>.
47       */
48      public static final String GET_METHOD = "GET";
49  
50      /**
51       * Constant representing the HTTP 'POST' request method, equal to <code>POST</code>.
52       */
53      public static final String POST_METHOD = "POST";
54  
55      /**
56       * The login url to used to authenticate a user, used when redirecting users if authentication is required.
57       */
58      private String loginUrl = DEFAULT_LOGIN_URL;
59  
60      /**
61       * Returns the login URL used to authenticate a user.
62       * <p/>
63       * Most Shiro filters use this url
64       * as the location to redirect a user when the filter requires authentication.  Unless overridden, the
65       * {@link #DEFAULT_LOGIN_URL DEFAULT_LOGIN_URL} is assumed, which can be overridden via
66       * {@link #setLoginUrl(String) setLoginUrl}.
67       *
68       * @return the login URL used to authenticate a user, used when redirecting users if authentication is required.
69       */
70      public String getLoginUrl() {
71          return loginUrl;
72      }
73  
74      /**
75       * Sets the login URL used to authenticate a user.
76       * <p/>
77       * Most Shiro filters use this url as the location to redirect a user when the filter requires
78       * authentication.  Unless overridden, the {@link #DEFAULT_LOGIN_URL DEFAULT_LOGIN_URL} is assumed.
79       *
80       * @param loginUrl the login URL used to authenticate a user, used when redirecting users if authentication is required.
81       */
82      public void setLoginUrl(String loginUrl) {
83          this.loginUrl = loginUrl;
84      }
85  
86      /**
87       * Convenience method that acquires the Subject associated with the request.
88       * <p/>
89       * The default implementation simply returns
90       * {@link org.apache.shiro.SecurityUtils#getSubject() SecurityUtils.getSubject()}.
91       *
92       * @param request  the incoming <code>ServletRequest</code>
93       * @param response the outgoing <code>ServletResponse</code>
94       * @return the Subject associated with the request.
95       */
96      protected Subject getSubject(ServletRequest request, ServletResponse response) {
97          return SecurityUtils.getSubject();
98      }
99  
100     /**
101      * Returns <code>true</code> if the request is allowed to proceed through the filter normally, or <code>false</code>
102      * if the request should be handled by the
103      * {@link #onAccessDenied(ServletRequest,ServletResponse,Object) onAccessDenied(request,response,mappedValue)}
104      * method instead.
105      *
106      * @param request     the incoming <code>ServletRequest</code>
107      * @param response    the outgoing <code>ServletResponse</code>
108      * @param mappedValue the filter-specific config value mapped to this filter in the URL rules mappings.
109      * @return <code>true</code> if the request should proceed through the filter normally, <code>false</code> if the
110      *         request should be processed by this filter's
111      *         {@link #onAccessDenied(ServletRequest,ServletResponse,Object)} method instead.
112      * @throws Exception if an error occurs during processing.
113      */
114     protected abstract boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception;
115 
116     /**
117      * Processes requests where the subject was denied access as determined by the
118      * {@link #isAccessAllowed(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Object) isAccessAllowed}
119      * method, retaining the {@code mappedValue} that was used during configuration.
120      * <p/>
121      * This method immediately delegates to {@link #onAccessDenied(ServletRequest,ServletResponse)} as a
122      * convenience in that most post-denial behavior does not need the mapped config again.
123      *
124      * @param request     the incoming <code>ServletRequest</code>
125      * @param response    the outgoing <code>ServletResponse</code>
126      * @param mappedValue the config specified for the filter in the matching request's filter chain.
127      * @return <code>true</code> if the request should continue to be processed; false if the subclass will
128      *         handle/render the response directly.
129      * @throws Exception if there is an error processing the request.
130      * @since 1.0
131      */
132     protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
133         return onAccessDenied(request, response);
134     }
135 
136     /**
137      * Processes requests where the subject was denied access as determined by the
138      * {@link #isAccessAllowed(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Object) isAccessAllowed}
139      * method.
140      *
141      * @param request  the incoming <code>ServletRequest</code>
142      * @param response the outgoing <code>ServletResponse</code>
143      * @return <code>true</code> if the request should continue to be processed; false if the subclass will
144      *         handle/render the response directly.
145      * @throws Exception if there is an error processing the request.
146      */
147     protected abstract boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception;
148 
149     /**
150      * Returns <code>true</code> if
151      * {@link #isAccessAllowed(ServletRequest,ServletResponse,Object) isAccessAllowed(Request,Response,Object)},
152      * otherwise returns the result of
153      * {@link #onAccessDenied(ServletRequest,ServletResponse,Object) onAccessDenied(Request,Response,Object)}.
154      *
155      * @return <code>true</code> if
156      *         {@link #isAccessAllowed(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Object) isAccessAllowed},
157      *         otherwise returns the result of
158      *         {@link #onAccessDenied(javax.servlet.ServletRequest, javax.servlet.ServletResponse) onAccessDenied}.
159      * @throws Exception if an error occurs.
160      */
161     public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
162         return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
163     }
164 
165     /**
166      * Returns <code>true</code> if the incoming request is a login request, <code>false</code> otherwise.
167      * <p/>
168      * The default implementation merely returns <code>true</code> if the incoming request matches the configured
169      * {@link #getLoginUrl() loginUrl} by calling
170      * <code>{@link #pathsMatch(String, String) pathsMatch(loginUrl, request)}</code>.
171      *
172      * @param request  the incoming <code>ServletRequest</code>
173      * @param response the outgoing <code>ServletResponse</code>
174      * @return <code>true</code> if the incoming request is a login request, <code>false</code> otherwise.
175      */
176     protected boolean isLoginRequest(ServletRequest request, ServletResponse response) {
177         return pathsMatch(getLoginUrl(), request);
178     }
179 
180     /**
181      * Convenience method for subclasses to use when a login redirect is required.
182      * <p/>
183      * This implementation simply calls {@link #saveRequest(javax.servlet.ServletRequest) saveRequest(request)}
184      * and then {@link #redirectToLogin(javax.servlet.ServletRequest, javax.servlet.ServletResponse) redirectToLogin(request,response)}.
185      *
186      * @param request  the incoming <code>ServletRequest</code>
187      * @param response the outgoing <code>ServletResponse</code>
188      * @throws IOException if an error occurs.
189      */
190     protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
191         saveRequest(request);
192         redirectToLogin(request, response);
193     }
194 
195     /**
196      * Convenience method merely delegates to
197      * {@link WebUtils#saveRequest(javax.servlet.ServletRequest) WebUtils.saveRequest(request)} to save the request
198      * state for reuse later.  This is mostly used to retain user request state when a redirect is issued to
199      * return the user to their originally requested url/resource.
200      * <p/>
201      * If you need to save and then immediately redirect the user to login, consider using
202      * {@link #saveRequestAndRedirectToLogin(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
203      * saveRequestAndRedirectToLogin(request,response)} directly.
204      *
205      * @param request the incoming ServletRequest to save for re-use later (for example, after a redirect).
206      */
207     protected void saveRequest(ServletRequest request) {
208         WebUtils.saveRequest(request);
209     }
210 
211     /**
212      * Convenience method for subclasses that merely acquires the {@link #getLoginUrl() getLoginUrl} and redirects
213      * the request to that url.
214      * <p/>
215      * <b>N.B.</b>  If you want to issue a redirect with the intention of allowing the user to then return to their
216      * originally requested URL, don't use this method directly.  Instead you should call
217      * {@link #saveRequestAndRedirectToLogin(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
218      * saveRequestAndRedirectToLogin(request,response)}, which will save the current request state so that it can
219      * be reconstructed and re-used after a successful login.
220      *
221      * @param request  the incoming <code>ServletRequest</code>
222      * @param response the outgoing <code>ServletResponse</code>
223      * @throws IOException if an error occurs.
224      */
225     protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
226         String loginUrl = getLoginUrl();
227         WebUtils.issueRedirect(request, response, loginUrl);
228     }
229 
230 }