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 }