|
|
|
|
@ -9,7 +9,6 @@ import net.sf.acegisecurity.context.ContextHolder;
@@ -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;
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 { } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|