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 */
019 package org.apache.shiro.web.filter;
020
021 import org.apache.shiro.SecurityUtils;
022 import org.apache.shiro.subject.Subject;
023 import org.apache.shiro.web.util.WebUtils;
024
025 import javax.servlet.ServletRequest;
026 import javax.servlet.ServletResponse;
027 import java.io.IOException;
028
029 /**
030 * Superclass for any filter that controls access to a resource and may redirect the user to the login page
031 * if they are not authenticated. This superclass provides the method
032 * {@link #saveRequestAndRedirectToLogin(javax.servlet.ServletRequest, javax.servlet.ServletResponse)}
033 * which is used by many subclasses as the behavior when a user is unauthenticated.
034 *
035 * @since 0.9
036 */
037 public abstract class AccessControlFilter extends PathMatchingFilter {
038
039 /**
040 * Simple default login URL equal to <code>/login.jsp</code>, which can be overridden by calling the
041 * {@link #setLoginUrl(String) setLoginUrl} method.
042 */
043 public static final String DEFAULT_LOGIN_URL = "/login.jsp";
044
045 /**
046 * Constant representing the HTTP 'GET' request method, equal to <code>GET</code>.
047 */
048 public static final String GET_METHOD = "GET";
049
050 /**
051 * Constant representing the HTTP 'POST' request method, equal to <code>POST</code>.
052 */
053 public static final String POST_METHOD = "POST";
054
055 /**
056 * The login url to used to authenticate a user, used when redirecting users if authentication is required.
057 */
058 private String loginUrl = DEFAULT_LOGIN_URL;
059
060 /**
061 * Returns the login URL used to authenticate a user.
062 * <p/>
063 * Most Shiro filters use this url
064 * as the location to redirect a user when the filter requires authentication. Unless overridden, the
065 * {@link #DEFAULT_LOGIN_URL DEFAULT_LOGIN_URL} is assumed, which can be overridden via
066 * {@link #setLoginUrl(String) setLoginUrl}.
067 *
068 * @return the login URL used to authenticate a user, used when redirecting users if authentication is required.
069 */
070 public String getLoginUrl() {
071 return loginUrl;
072 }
073
074 /**
075 * Sets the login URL used to authenticate a user.
076 * <p/>
077 * Most Shiro filters use this url as the location to redirect a user when the filter requires
078 * authentication. Unless overridden, the {@link #DEFAULT_LOGIN_URL DEFAULT_LOGIN_URL} is assumed.
079 *
080 * @param loginUrl the login URL used to authenticate a user, used when redirecting users if authentication is required.
081 */
082 public void setLoginUrl(String loginUrl) {
083 this.loginUrl = loginUrl;
084 }
085
086 /**
087 * Convenience method that acquires the Subject associated with the request.
088 * <p/>
089 * The default implementation simply returns
090 * {@link org.apache.shiro.SecurityUtils#getSubject() SecurityUtils.getSubject()}.
091 *
092 * @param request the incoming <code>ServletRequest</code>
093 * @param response the outgoing <code>ServletResponse</code>
094 * @return the Subject associated with the request.
095 */
096 protected Subject getSubject(ServletRequest request, ServletResponse response) {
097 return SecurityUtils.getSubject();
098 }
099
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 }