diff --git a/core/src/main/java/org/acegisecurity/ui/x509/X509ProcessingFilter.java b/core/src/main/java/org/acegisecurity/ui/x509/X509ProcessingFilter.java index 5b909b40be..65c59b8f26 100644 --- a/core/src/main/java/org/acegisecurity/ui/x509/X509ProcessingFilter.java +++ b/core/src/main/java/org/acegisecurity/ui/x509/X509ProcessingFilter.java @@ -9,7 +9,6 @@ import net.sf.acegisecurity.context.ContextHolder; import net.sf.acegisecurity.context.security.SecureContext; import net.sf.acegisecurity.context.security.SecureContextUtils; import net.sf.acegisecurity.providers.x509.X509AuthenticationToken; -import net.sf.acegisecurity.providers.x509.X509AuthenticationProvider; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -22,8 +21,8 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; /** - * Processes the X.509 certificate submitted by a client - typically - * when HTTPS is used with client-authentiction enabled. + * Processes the X.509 certificate submitted by a client browser + * when HTTPS is used with client-authentication enabled. *

* An {@link X509AuthenticationToken} is created with the certificate * as the credentials. @@ -47,8 +46,11 @@ public class X509ProcessingFilter implements Filter, InitializingBean { private static final Log logger = LogFactory.getLog(X509ProcessingFilter.class); + //~ Instance fields ======================================================== + private AuthenticationManager authenticationManager; + //~ Methods ================================================================ public void setAuthenticationManager(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; @@ -59,6 +61,22 @@ public class X509ProcessingFilter implements Filter, InitializingBean { throw new IllegalArgumentException("An AuthenticationManager must be set"); } + /** + * This method first checks for an existing, non-null authentication in the + * secure context. If one is found it does nothing. + *

+ * If no authentication object exists, it attempts to obtain the client + * authentication certificate from the request. If there is no certificate + * present then authentication is skipped. Otherwise a new authentication + * request containing the certificate will be passed to the configured + * {@link AuthenticationManager}. + *

+ *

+ * If authentication is successful the returned token will be stored in + * the secure context. Otherwise it will be set to null. + * In either case, the request proceeds through the filter chain. + *

+ */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { if (!(request instanceof HttpServletRequest)) { throw new ServletException("Can only process HttpServletRequest"); @@ -68,44 +86,77 @@ public class X509ProcessingFilter implements Filter, InitializingBean { throw new ServletException("Can only process HttpServletResponse"); } + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; SecureContext ctx = SecureContextUtils.getSecureContext(); - logger.debug("Checking secure context: " + ctx); + logger.debug("Checking secure context token: " + ctx.getAuthentication()); + if(ctx.getAuthentication() == null) { - attemptAuthentication((HttpServletRequest)request); - } + Authentication authResult = null; + X509Certificate clientCertificate = extractClientCertificate(httpRequest); + + try { + X509AuthenticationToken authRequest = new X509AuthenticationToken(clientCertificate); + // authRequest.setDetails(new WebAuthenticationDetails(request)); + authResult = authenticationManager.authenticate(authRequest); + successfulAuthentication(httpRequest, httpResponse, authResult); + } catch (AuthenticationException failed) { + unsuccessfulAuthentication(httpRequest, httpResponse, failed); + } + } filterChain.doFilter(request, response); } - /** - * - * @param request the request containing the client certificate - * @return - * @throws AuthenticationException if the authentication manager rejects the certificate for some reason. - */ - public Authentication attemptAuthentication(HttpServletRequest request) throws AuthenticationException { + private X509Certificate extractClientCertificate(HttpServletRequest request) { X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate"); - X509Certificate clientCertificate = null; - if(certs != null && certs.length > 0) { - clientCertificate = certs[0]; - logger.debug("Authenticating with certificate " + clientCertificate); - } else { - logger.warn("No client certificate found in Request."); + return certs[0]; + } + + if(logger.isDebugEnabled()) + logger.debug("No client certificate found in request, authentication will fail."); + + return null; + } + + /** + * Puts the Authentication instance returned by the authentication manager into + * the secure context. + */ + protected void successfulAuthentication(HttpServletRequest request, + HttpServletResponse response, Authentication authResult) + throws IOException { + + if (logger.isDebugEnabled()) { + logger.debug("Authentication success: " + authResult); } - // TODO: warning is probably superfluous, as it may get called when a non-protected URL is used and no certificate is present. + SecureContext sc = SecureContextUtils.getSecureContext(); + sc.setAuthentication(authResult); + } - X509AuthenticationToken authRequest = new X509AuthenticationToken(clientCertificate); + /** + * Ensures the authentication object in the secure context is set to null when authentication fails. + * + */ + protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) { + SecureContext sc = SecureContextUtils.getSecureContext(); - // authRequest.setDetails(new WebAuthenticationDetails(request)); + sc.setAuthentication(null); + ContextHolder.setContext(sc); - return authenticationManager.authenticate(authRequest); + if (logger.isDebugEnabled()) { + logger.debug("Updated ContextHolder to contain null Authentication"); + } + + request.getSession().setAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY, failed); } + public void init(FilterConfig filterConfig) throws ServletException { }