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.filter; 020 021import org.apache.shiro.SecurityUtils; 022import org.apache.shiro.subject.Subject; 023import org.apache.shiro.web.util.WebUtils; 024 025import javax.servlet.ServletRequest; 026import javax.servlet.ServletResponse; 027import 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 */ 037public 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}