|
@@ -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.
|
|
|
* <p>
|
|
|
* 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.
|
|
|
+ * <p>
|
|
|
+ * 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}.
|
|
|
+ * </p>
|
|
|
+ * <p>
|
|
|
+ * 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.
|
|
|
+ * </p>
|
|
|
+ */
|
|
|
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 <code>Authentication</code> 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 { }
|
|
|
|
|
|
|