001: /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
002: *
003: * Licensed under the Apache License, Version 2.0 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at
006: *
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015:
016: package org.acegisecurity.ui;
017:
018: import org.acegisecurity.AccessDeniedException;
019: import org.acegisecurity.AcegiSecurityException;
020: import org.acegisecurity.AuthenticationException;
021: import org.acegisecurity.AuthenticationTrustResolver;
022: import org.acegisecurity.AuthenticationTrustResolverImpl;
023: import org.acegisecurity.InsufficientAuthenticationException;
024:
025: import org.acegisecurity.context.SecurityContextHolder;
026:
027: import org.acegisecurity.ui.savedrequest.SavedRequest;
028:
029: import org.acegisecurity.util.PortResolver;
030: import org.acegisecurity.util.PortResolverImpl;
031:
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034:
035: import org.springframework.beans.factory.InitializingBean;
036: import org.springframework.context.ApplicationContext;
037:
038: import org.springframework.util.Assert;
039:
040: import java.io.IOException;
041: import java.util.Map;
042:
043: import javax.servlet.Filter;
044: import javax.servlet.FilterChain;
045: import javax.servlet.FilterConfig;
046: import javax.servlet.ServletException;
047: import javax.servlet.ServletRequest;
048: import javax.servlet.ServletResponse;
049: import javax.servlet.http.HttpServletRequest;
050: import javax.servlet.http.HttpServletResponse;
051:
052: /**
053: * Handles any <code>AccessDeniedException</code> and <code>AuthenticationException</code> thrown within the
054: * filter chain.
055: * <p>
056: * This filter is necessary because it provides the bridge between Java exceptions and HTTP responses.
057: * It is solely concerned with maintaining the user interface. This filter does not do any actual security enforcement.
058: * </p>
059: * <p>
060: * If an {@link AuthenticationException} is detected, the filter will launch the <code>authenticationEntryPoint</code>.
061: * This allows common handling of authentication failures originating from any subclass of
062: * {@link org.acegisecurity.intercept.AbstractSecurityInterceptor}.
063: * </p>
064: * <p>
065: * If an {@link AccessDeniedException} is detected, the filter will determine whether or not the user is an anonymous
066: * user. If they are an anonymous user, the <code>authenticationEntryPoint</code> will be launched. If they are not
067: * an anonymous user, the filter will delegate to the {@link org.acegisecurity.ui.AccessDeniedHandler}.
068: * By default the filter will use {@link org.acegisecurity.ui.AccessDeniedHandlerImpl}.
069: * </p>
070: * <p>
071: * To use this filter, it is necessary to specify the following properties:
072: * </p>
073: * <ul>
074: * <li><code>authenticationEntryPoint</code> indicates the handler that
075: * should commence the authentication process if an
076: * <code>AuthenticationException</code> is detected. Note that this may also
077: * switch the current protocol from http to https for an SSL login.</li>
078: * <li><code>portResolver</code> is used to determine the "real" port that a
079: * request was received on.</li>
080: * </ul>
081: * <P>
082: * <B>Do not use this class directly.</B> Instead configure
083: * <code>web.xml</code> to use the {@link
084: * org.acegisecurity.util.FilterToBeanProxy}.
085: * </p>
086: *
087: * @author Ben Alex
088: * @author colin sampaleanu
089: * @version $Id: ExceptionTranslationFilter.java 2134 2007-09-19 16:41:06Z luke_t $
090: */
091: public class ExceptionTranslationFilter implements Filter,
092: InitializingBean {
093:
094: //~ Static fields/initializers =====================================================================================
095:
096: private static final Log logger = LogFactory
097: .getLog(ExceptionTranslationFilter.class);
098:
099: //~ Instance fields ================================================================================================
100:
101: private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
102: private AuthenticationEntryPoint authenticationEntryPoint;
103: private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
104: private PortResolver portResolver = new PortResolverImpl();
105: private boolean createSessionAllowed = true;
106:
107: //~ Methods ========================================================================================================
108:
109: public void afterPropertiesSet() throws Exception {
110: Assert.notNull(authenticationEntryPoint,
111: "authenticationEntryPoint must be specified");
112: Assert.notNull(portResolver, "portResolver must be specified");
113: Assert.notNull(authenticationTrustResolver,
114: "authenticationTrustResolver must be specified");
115: }
116:
117: public void doFilter(ServletRequest request,
118: ServletResponse response, FilterChain chain)
119: throws IOException, ServletException {
120: if (!(request instanceof HttpServletRequest)) {
121: throw new ServletException("HttpServletRequest required");
122: }
123:
124: if (!(response instanceof HttpServletResponse)) {
125: throw new ServletException("HttpServletResponse required");
126: }
127:
128: try {
129: chain.doFilter(request, response);
130:
131: if (logger.isDebugEnabled()) {
132: logger.debug("Chain processed normally");
133: }
134: } catch (AuthenticationException ex) {
135: handleException(request, response, chain, ex);
136: } catch (AccessDeniedException ex) {
137: handleException(request, response, chain, ex);
138: } catch (ServletException ex) {
139: if (ex.getRootCause() instanceof AuthenticationException
140: || ex.getRootCause() instanceof AccessDeniedException) {
141: handleException(request, response, chain,
142: (AcegiSecurityException) ex.getRootCause());
143: } else {
144: throw ex;
145: }
146: } catch (IOException ex) {
147: throw ex;
148: }
149: }
150:
151: public AuthenticationEntryPoint getAuthenticationEntryPoint() {
152: return authenticationEntryPoint;
153: }
154:
155: public AuthenticationTrustResolver getAuthenticationTrustResolver() {
156: return authenticationTrustResolver;
157: }
158:
159: public PortResolver getPortResolver() {
160: return portResolver;
161: }
162:
163: private void handleException(ServletRequest request,
164: ServletResponse response, FilterChain chain,
165: AcegiSecurityException exception) throws IOException,
166: ServletException {
167: if (exception instanceof AuthenticationException) {
168: if (logger.isDebugEnabled()) {
169: logger
170: .debug(
171: "Authentication exception occurred; redirecting to authentication entry point",
172: exception);
173: }
174:
175: sendStartAuthentication(request, response, chain,
176: (AuthenticationException) exception);
177: } else if (exception instanceof AccessDeniedException) {
178: if (authenticationTrustResolver
179: .isAnonymous(SecurityContextHolder.getContext()
180: .getAuthentication())) {
181: if (logger.isDebugEnabled()) {
182: logger
183: .debug(
184: "Access is denied (user is anonymous); redirecting to authentication entry point",
185: exception);
186: }
187:
188: sendStartAuthentication(
189: request,
190: response,
191: chain,
192: new InsufficientAuthenticationException(
193: "Full authentication is required to access this resource"));
194: } else {
195: if (logger.isDebugEnabled()) {
196: logger
197: .debug(
198: "Access is denied (user is not anonymous); delegating to AccessDeniedHandler",
199: exception);
200: }
201:
202: accessDeniedHandler.handle(request, response,
203: (AccessDeniedException) exception);
204: }
205: }
206: }
207:
208: /**
209: * If <code>true</code>, indicates that <code>SecurityEnforcementFilter</code> is permitted to store the target
210: * URL and exception information in the <code>HttpSession</code> (the default).
211: * In situations where you do not wish to unnecessarily create <code>HttpSession</code>s - because the user agent
212: * will know the failed URL, such as with BASIC or Digest authentication - you may wish to
213: * set this property to <code>false</code>. Remember to also set the
214: * {@link org.acegisecurity.context.HttpSessionContextIntegrationFilter#allowSessionCreation}
215: * to <code>false</code> if you set this property to <code>false</code>.
216: *
217: * @return <code>true</code> if the <code>HttpSession</code> will be
218: * used to store information about the failed request, <code>false</code>
219: * if the <code>HttpSession</code> will not be used
220: */
221: public boolean isCreateSessionAllowed() {
222: return createSessionAllowed;
223: }
224:
225: protected void sendStartAuthentication(ServletRequest request,
226: ServletResponse response, FilterChain chain,
227: AuthenticationException reason) throws ServletException,
228: IOException {
229: HttpServletRequest httpRequest = (HttpServletRequest) request;
230:
231: SavedRequest savedRequest = new SavedRequest(httpRequest,
232: portResolver);
233:
234: if (logger.isDebugEnabled()) {
235: logger
236: .debug("Authentication entry point being called; SavedRequest added to Session: "
237: + savedRequest);
238: }
239:
240: if (createSessionAllowed) {
241: // Store the HTTP request itself. Used by AbstractProcessingFilter
242: // for redirection after successful authentication (SEC-29)
243: httpRequest.getSession().setAttribute(
244: AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY,
245: savedRequest);
246: }
247:
248: // SEC-112: Clear the SecurityContextHolder's Authentication, as the
249: // existing Authentication is no longer considered valid
250: SecurityContextHolder.getContext().setAuthentication(null);
251:
252: authenticationEntryPoint.commence(httpRequest,
253: (HttpServletResponse) response, reason);
254: }
255:
256: public void setAccessDeniedHandler(
257: AccessDeniedHandler accessDeniedHandler) {
258: Assert.notNull(accessDeniedHandler,
259: "AccessDeniedHandler required");
260: this .accessDeniedHandler = accessDeniedHandler;
261: }
262:
263: public void setAuthenticationEntryPoint(
264: AuthenticationEntryPoint authenticationEntryPoint) {
265: this .authenticationEntryPoint = authenticationEntryPoint;
266: }
267:
268: public void setAuthenticationTrustResolver(
269: AuthenticationTrustResolver authenticationTrustResolver) {
270: this .authenticationTrustResolver = authenticationTrustResolver;
271: }
272:
273: public void setCreateSessionAllowed(boolean createSessionAllowed) {
274: this .createSessionAllowed = createSessionAllowed;
275: }
276:
277: public void setPortResolver(PortResolver portResolver) {
278: this .portResolver = portResolver;
279: }
280:
281: public void init(FilterConfig filterConfig) throws ServletException {
282: }
283:
284: public void destroy() {
285: }
286: }
|