mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-26 22:02:41 +00:00
SEC-257: ExceptionTranslationFilter to use AccessDeniedHandler.
This commit is contained in:
parent
9a90e4e1aa
commit
cc07f620df
@ -0,0 +1,50 @@
|
|||||||
|
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.acegisecurity.ui;
|
||||||
|
|
||||||
|
import org.acegisecurity.AccessDeniedException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by {@link ExceptionTranslationFilter} to handle an
|
||||||
|
* <code>AccessDeniedException</code>.
|
||||||
|
*
|
||||||
|
* @author Ben Alex
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public interface AccessDeniedHandler {
|
||||||
|
//~ Methods ================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles an access denied failure.
|
||||||
|
*
|
||||||
|
* @param request that resulted in an <code>AccessDeniedException</code>
|
||||||
|
* @param response so that the user agent can be advised of the failure
|
||||||
|
* @param accessDeniedException that caused the invocation
|
||||||
|
*
|
||||||
|
* @throws IOException in the event of an IOException
|
||||||
|
* @throws ServletException in the event of a ServletException
|
||||||
|
*/
|
||||||
|
public void handle(ServletRequest request, ServletResponse response,
|
||||||
|
AccessDeniedException accessDeniedException)
|
||||||
|
throws IOException, ServletException;
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.acegisecurity.ui;
|
||||||
|
|
||||||
|
import org.acegisecurity.AccessDeniedException;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.RequestDispatcher;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base implementation of {@link AccessDeniedHandler}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This implementation sends a 403 (SC_FORBIDDEN) HTTP error code. In addition,
|
||||||
|
* if a {@link #errorPage} is defined, the implementation will perform a
|
||||||
|
* request dispatcher "forward" to the specified error page view. Being a
|
||||||
|
* "forward", the <code>SecurityContextHolder</code> will remain populated.
|
||||||
|
* This is of benefit if the view (or a tag library or macro) wishes to access
|
||||||
|
* the <code>SecurityContextHolder</code>. The request scope will also be
|
||||||
|
* populated with the exception itself, available from the key {@link
|
||||||
|
* #ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Ben Alex
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
|
||||||
|
//~ Static fields/initializers =============================================
|
||||||
|
|
||||||
|
public static final String ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY = "ACEGI_SECURITY_403_EXCEPTION";
|
||||||
|
protected final static Log logger = LogFactory.getLog(AccessDeniedHandlerImpl.class);
|
||||||
|
|
||||||
|
//~ Instance fields ========================================================
|
||||||
|
|
||||||
|
private String errorPage;
|
||||||
|
|
||||||
|
//~ Methods ================================================================
|
||||||
|
|
||||||
|
public void handle(ServletRequest request, ServletResponse response,
|
||||||
|
AccessDeniedException accessDeniedException)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
if (errorPage != null) {
|
||||||
|
// Put exception into request scope (perhaps of use to a view)
|
||||||
|
((HttpServletRequest) request).setAttribute(ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY,
|
||||||
|
accessDeniedException);
|
||||||
|
|
||||||
|
// Perform RequestDispatcher "forward"
|
||||||
|
RequestDispatcher rd = request.getRequestDispatcher(errorPage);
|
||||||
|
|
||||||
|
try {
|
||||||
|
rd.forward(request, response);
|
||||||
|
((HttpServletResponse)response).setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
} catch (Exception responseCommitted) {
|
||||||
|
if (logger.isErrorEnabled()) {
|
||||||
|
logger.error("Error processing " + request.toString(),
|
||||||
|
responseCommitted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send 403 (we do this after response has been written)
|
||||||
|
((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||||
|
accessDeniedException.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The error page to use. Must begin with a "/" and is interpreted relative
|
||||||
|
* to the current context root.
|
||||||
|
*
|
||||||
|
* @param errorPage the dispatcher path to display
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if the argument doesn't comply with the
|
||||||
|
* above limitations
|
||||||
|
*/
|
||||||
|
public void setErrorPage(String errorPage) {
|
||||||
|
if ((errorPage != null) && !errorPage.startsWith("/")) {
|
||||||
|
throw new IllegalArgumentException("ErrorPage must begin with '/'");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.errorPage = errorPage;
|
||||||
|
}
|
||||||
|
}
|
@ -69,13 +69,9 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
* If an {@link AccessDeniedException} is detected, the filter will determine
|
* If an {@link AccessDeniedException} is detected, the filter will determine
|
||||||
* whether or not the user is an anonymous user. If they are an anonymous
|
* whether or not the user is an anonymous user. If they are an anonymous
|
||||||
* user, the <code>authenticationEntryPoint</code> will be launched. If they
|
* user, the <code>authenticationEntryPoint</code> will be launched. If they
|
||||||
* are not an anonymous user, the filter will respond with a
|
* are not an anonymous user, the filter will delegate to the {@link
|
||||||
* <code>HttpServletResponse.SC_FORBIDDEN</code> (403 error). In addition,
|
* org.acegisecurity.ui.AccessDeniedHandler}. By default the filter will use
|
||||||
* the <code>AccessDeniedException</code> itself will be placed in the
|
* {@link org.acegisecurity.ui.AccessDeniedHandlerImpl}.
|
||||||
* <code>HttpSession</code> attribute keyed against {@link
|
|
||||||
* #ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY} (to allow access to the stack
|
|
||||||
* trace etc). Again, this allows common access denied handling irrespective
|
|
||||||
* of the originating security interceptor.
|
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
@ -109,10 +105,10 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean {
|
|||||||
//~ Static fields/initializers =============================================
|
//~ Static fields/initializers =============================================
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(ExceptionTranslationFilter.class);
|
private static final Log logger = LogFactory.getLog(ExceptionTranslationFilter.class);
|
||||||
public static final String ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY = "ACEGI_SECURITY_403_EXCEPTION";
|
|
||||||
|
|
||||||
//~ Instance fields ========================================================
|
//~ Instance fields ========================================================
|
||||||
|
|
||||||
|
private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
|
||||||
private AuthenticationEntryPoint authenticationEntryPoint;
|
private AuthenticationEntryPoint authenticationEntryPoint;
|
||||||
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
||||||
private PortResolver portResolver = new PortResolverImpl();
|
private PortResolver portResolver = new PortResolverImpl();
|
||||||
@ -199,11 +195,11 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean {
|
|||||||
"Full authentication is required to access this resource"));
|
"Full authentication is required to access this resource"));
|
||||||
} else {
|
} else {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Access is denied (user is not anonymous); sending back forbidden response",
|
logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler",
|
||||||
exception);
|
exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendAccessDeniedError(request, response, chain,
|
accessDeniedHandler.handle(request, response,
|
||||||
(AccessDeniedException) exception);
|
(AccessDeniedException) exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,20 +227,6 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean {
|
|||||||
return createSessionAllowed;
|
return createSessionAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void sendAccessDeniedError(ServletRequest request,
|
|
||||||
ServletResponse response, FilterChain chain,
|
|
||||||
AccessDeniedException accessDenied)
|
|
||||||
throws ServletException, IOException {
|
|
||||||
if (createSessionAllowed) {
|
|
||||||
((HttpServletRequest) request).getSession()
|
|
||||||
.setAttribute(ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY,
|
|
||||||
accessDenied);
|
|
||||||
}
|
|
||||||
|
|
||||||
((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN,
|
|
||||||
accessDenied.getMessage()); // 403
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void sendStartAuthentication(ServletRequest request,
|
protected void sendStartAuthentication(ServletRequest request,
|
||||||
ServletResponse response, FilterChain chain,
|
ServletResponse response, FilterChain chain,
|
||||||
AuthenticationException reason) throws ServletException, IOException {
|
AuthenticationException reason) throws ServletException, IOException {
|
||||||
@ -274,6 +256,11 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean {
|
|||||||
(HttpServletResponse) response, reason);
|
(HttpServletResponse) response, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
|
||||||
|
Assert.notNull(accessDeniedHandler, "AccessDeniedHandler required");
|
||||||
|
this.accessDeniedHandler = accessDeniedHandler;
|
||||||
|
}
|
||||||
|
|
||||||
public void setAuthenticationEntryPoint(
|
public void setAuthenticationEntryPoint(
|
||||||
AuthenticationEntryPoint authenticationEntryPoint) {
|
AuthenticationEntryPoint authenticationEntryPoint) {
|
||||||
this.authenticationEntryPoint = authenticationEntryPoint;
|
this.authenticationEntryPoint = authenticationEntryPoint;
|
||||||
|
@ -113,17 +113,22 @@ public class ExceptionTranslationFilterTests extends TestCase {
|
|||||||
// Setup SecurityContextHolder, as filter needs to check if user is anonymous
|
// Setup SecurityContextHolder, as filter needs to check if user is anonymous
|
||||||
SecurityContextHolder.getContext().setAuthentication(null);
|
SecurityContextHolder.getContext().setAuthentication(null);
|
||||||
|
|
||||||
|
// Setup a new AccessDeniedHandlerImpl that will do a "forward"
|
||||||
|
AccessDeniedHandlerImpl adh = new AccessDeniedHandlerImpl();
|
||||||
|
adh.setErrorPage("/error.jsp");
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
|
ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
|
||||||
filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(
|
filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(
|
||||||
"/login.jsp"));
|
"/login.jsp"));
|
||||||
|
filter.setAccessDeniedHandler(adh);
|
||||||
|
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
filter.doFilter(request, response, chain);
|
filter.doFilter(request, response, chain);
|
||||||
assertEquals(403, response.getStatus());
|
assertEquals(403, response.getStatus());
|
||||||
assertEquals(AccessDeniedException.class,
|
assertEquals(AccessDeniedException.class,
|
||||||
request.getSession()
|
request.getAttribute(
|
||||||
.getAttribute(ExceptionTranslationFilter.ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY)
|
AccessDeniedHandlerImpl.ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY)
|
||||||
.getClass());
|
.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,11 @@ applications:
|
|||||||
AbstractProcessingFilter.onUnsuccessfulAuthentication(HttpServletRequest, HttpServletResponse)
|
AbstractProcessingFilter.onUnsuccessfulAuthentication(HttpServletRequest, HttpServletResponse)
|
||||||
has changed it signature (SEC-238). If subclassing, please override the new signature.
|
has changed it signature (SEC-238). If subclassing, please override the new signature.
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
ExceptionTranslationFilter no longer provides a sendAccessDenied() method. Use the
|
||||||
|
new AccessDeniedHandler instead if custom handling is required.
|
||||||
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
@ -98,11 +98,6 @@
|
|||||||
<welcome-file-list>
|
<welcome-file-list>
|
||||||
<welcome-file>index.jsp</welcome-file>
|
<welcome-file>index.jsp</welcome-file>
|
||||||
</welcome-file-list>
|
</welcome-file-list>
|
||||||
|
|
||||||
<error-page>
|
|
||||||
<error-code>403</error-code>
|
|
||||||
<location>/error.html</location>
|
|
||||||
</error-page>
|
|
||||||
|
|
||||||
<taglib>
|
<taglib>
|
||||||
<taglib-uri>/spring</taglib-uri>
|
<taglib-uri>/spring</taglib-uri>
|
||||||
|
16
samples/contacts/src/main/webapp/filter/accessDenied.jsp
Normal file
16
samples/contacts/src/main/webapp/filter/accessDenied.jsp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<%@ page import="org.acegisecurity.context.SecurityContextHolder" %>
|
||||||
|
<%@ page import="org.acegisecurity.Authentication" %>
|
||||||
|
<%@ page import="org.acegisecurity.ui.AccessDeniedHandlerImpl" %>
|
||||||
|
|
||||||
|
<h1>Sorry, access is denied</h1>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<%= request.getAttribute(AccessDeniedHandlerImpl.ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY)%>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
|
||||||
|
<% Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (auth != null) { %>
|
||||||
|
Authentication object as a String: <%= auth.toString() %><BR><BR>
|
||||||
|
<% } %>
|
@ -1,5 +0,0 @@
|
|||||||
<html>
|
|
||||||
<title>Access denied!</title>
|
|
||||||
<h1>Access Denied</h1>
|
|
||||||
We're sorry, but you are not authorized to perform the requested operation.
|
|
||||||
</html>
|
|
Loading…
x
Reference in New Issue
Block a user