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
|
||||
* whether or not the user is an anonymous user. If they are an anonymous
|
||||
* user, the <code>authenticationEntryPoint</code> will be launched. If they
|
||||
* are not an anonymous user, the filter will respond with a
|
||||
* <code>HttpServletResponse.SC_FORBIDDEN</code> (403 error). In addition,
|
||||
* the <code>AccessDeniedException</code> itself will be placed in the
|
||||
* <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.
|
||||
* are not an anonymous user, the filter will delegate to the {@link
|
||||
* org.acegisecurity.ui.AccessDeniedHandler}. By default the filter will use
|
||||
* {@link org.acegisecurity.ui.AccessDeniedHandlerImpl}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
|
@ -109,10 +105,10 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean {
|
|||
//~ Static fields/initializers =============================================
|
||||
|
||||
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 ========================================================
|
||||
|
||||
private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
|
||||
private AuthenticationEntryPoint authenticationEntryPoint;
|
||||
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
||||
private PortResolver portResolver = new PortResolverImpl();
|
||||
|
@ -199,11 +195,11 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean {
|
|||
"Full authentication is required to access this resource"));
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
sendAccessDeniedError(request, response, chain,
|
||||
accessDeniedHandler.handle(request, response,
|
||||
(AccessDeniedException) exception);
|
||||
}
|
||||
}
|
||||
|
@ -231,20 +227,6 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean {
|
|||
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,
|
||||
ServletResponse response, FilterChain chain,
|
||||
AuthenticationException reason) throws ServletException, IOException {
|
||||
|
@ -274,6 +256,11 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean {
|
|||
(HttpServletResponse) response, reason);
|
||||
}
|
||||
|
||||
public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
|
||||
Assert.notNull(accessDeniedHandler, "AccessDeniedHandler required");
|
||||
this.accessDeniedHandler = accessDeniedHandler;
|
||||
}
|
||||
|
||||
public void setAuthenticationEntryPoint(
|
||||
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
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
|
||||
// Setup a new AccessDeniedHandlerImpl that will do a "forward"
|
||||
AccessDeniedHandlerImpl adh = new AccessDeniedHandlerImpl();
|
||||
adh.setErrorPage("/error.jsp");
|
||||
|
||||
// Test
|
||||
ExceptionTranslationFilter filter = new ExceptionTranslationFilter();
|
||||
filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint(
|
||||
"/login.jsp"));
|
||||
filter.setAccessDeniedHandler(adh);
|
||||
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
filter.doFilter(request, response, chain);
|
||||
assertEquals(403, response.getStatus());
|
||||
assertEquals(AccessDeniedException.class,
|
||||
request.getSession()
|
||||
.getAttribute(ExceptionTranslationFilter.ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY)
|
||||
request.getAttribute(
|
||||
AccessDeniedHandlerImpl.ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY)
|
||||
.getClass());
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,11 @@ applications:
|
|||
AbstractProcessingFilter.onUnsuccessfulAuthentication(HttpServletRequest, HttpServletResponse)
|
||||
has changed it signature (SEC-238). If subclassing, please override the new signature.
|
||||
</li>
|
||||
|
||||
|
||||
<li>
|
||||
ExceptionTranslationFilter no longer provides a sendAccessDenied() method. Use the
|
||||
new AccessDeniedHandler instead if custom handling is required.
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
|
|
@ -98,11 +98,6 @@
|
|||
<welcome-file-list>
|
||||
<welcome-file>index.jsp</welcome-file>
|
||||
</welcome-file-list>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<taglib>
|
||||
<taglib-uri>/spring</taglib-uri>
|
||||
|
|
|
@ -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…
Reference in New Issue