Source code: net/sf/acegisecurity/ui/x509/X509ProcessingFilter.java
1 /* Copyright 2004, 2005 Acegi Technology Pty Limited
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package net.sf.acegisecurity.ui.x509;
17
18 import net.sf.acegisecurity.ui.AbstractProcessingFilter;
19 import net.sf.acegisecurity.ui.WebAuthenticationDetails;
20 import net.sf.acegisecurity.Authentication;
21 import net.sf.acegisecurity.AuthenticationException;
22 import net.sf.acegisecurity.AuthenticationManager;
23 import net.sf.acegisecurity.context.ContextHolder;
24 import net.sf.acegisecurity.context.security.SecureContext;
25 import net.sf.acegisecurity.context.security.SecureContextUtils;
26 import net.sf.acegisecurity.providers.x509.X509AuthenticationToken;
27
28 import javax.servlet.http.HttpServletRequest;
29 import javax.servlet.http.HttpServletResponse;
30 import javax.servlet.*;
31 import java.security.cert.X509Certificate;
32 import java.io.IOException;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.springframework.beans.factory.InitializingBean;
37 import org.springframework.util.Assert;
38
39 /**
40 * Processes the X.509 certificate submitted by a client browser
41 * when HTTPS is used with client-authentication enabled.
42 * <p>
43 * An {@link X509AuthenticationToken} is created with the certificate
44 * as the credentials.
45 * </p>
46 * <p>
47 * The configured authentication manager is expected to supply a
48 * provider which can handle this token (usually an instance of
49 * {@link net.sf.acegisecurity.providers.x509.X509AuthenticationProvider}).
50 * </p>
51 *
52 * <p>
53 * <b>Do not use this class directly.</b> Instead configure
54 * <code>web.xml</code> to use the {@link
55 * net.sf.acegisecurity.util.FilterToBeanProxy}.
56 * </p>
57 *
58 * @author Luke Taylor
59 * @version $Id: X509ProcessingFilter.java,v 1.5 2005/03/18 01:00:34 luke_t Exp $
60 */
61 public class X509ProcessingFilter implements Filter, InitializingBean {
62 //~ Static fields/initializers =============================================
63
64 private static final Log logger = LogFactory.getLog(X509ProcessingFilter.class);
65
66 //~ Instance fields ========================================================
67
68 private AuthenticationManager authenticationManager;
69
70 //~ Methods ================================================================
71
72 public void setAuthenticationManager(AuthenticationManager authenticationManager) {
73 this.authenticationManager = authenticationManager;
74 }
75
76 public void afterPropertiesSet() throws Exception {
77 Assert.notNull(authenticationManager, "An AuthenticationManager must be set");
78 }
79
80 /**
81 * This method first checks for an existing, non-null authentication in the
82 * secure context. If one is found it does nothing.
83 * <p>
84 * If no authentication object exists, it attempts to obtain the client
85 * authentication certificate from the request. If there is no certificate
86 * present then authentication is skipped. Otherwise a new authentication
87 * request containing the certificate will be passed to the configured
88 * {@link AuthenticationManager}.
89 * </p>
90 * <p>
91 * If authentication is successful the returned token will be stored in
92 * the secure context. Otherwise it will be set to null.
93 * In either case, the request proceeds through the filter chain.
94 * </p>
95 */
96 public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
97 if (!(request instanceof HttpServletRequest)) {
98 throw new ServletException("Can only process HttpServletRequest");
99 }
100
101 if (!(response instanceof HttpServletResponse)) {
102 throw new ServletException("Can only process HttpServletResponse");
103 }
104
105 HttpServletRequest httpRequest = (HttpServletRequest) request;
106 HttpServletResponse httpResponse = (HttpServletResponse) response;
107
108 SecureContext ctx = SecureContextUtils.getSecureContext();
109
110 logger.debug("Checking secure context token: " + ctx.getAuthentication());
111
112 if (ctx.getAuthentication() == null) {
113
114 Authentication authResult = null;
115 X509Certificate clientCertificate = extractClientCertificate(httpRequest);
116
117 try {
118 X509AuthenticationToken authRequest = new X509AuthenticationToken(clientCertificate);
119
120 authRequest.setDetails(new WebAuthenticationDetails(httpRequest));
121 authResult = authenticationManager.authenticate(authRequest);
122 successfulAuthentication(httpRequest, httpResponse, authResult);
123 } catch (AuthenticationException failed) {
124 unsuccessfulAuthentication(httpRequest, httpResponse, failed);
125 }
126 }
127 filterChain.doFilter(request, response);
128 }
129
130 private X509Certificate extractClientCertificate(HttpServletRequest request) {
131 X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
132
133 if (certs != null && certs.length > 0) {
134 return certs[0];
135 }
136
137 if (logger.isDebugEnabled()) {
138 logger.debug("No client certificate found in request.");
139 }
140
141 return null;
142 }
143
144 /**
145 * Puts the <code>Authentication</code> instance returned by the authentication manager into
146 * the secure context.
147 */
148 protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult)
149 throws IOException {
150
151 if (logger.isDebugEnabled()) {
152 logger.debug("Authentication success: " + authResult);
153 }
154 SecureContext sc = SecureContextUtils.getSecureContext();
155 sc.setAuthentication(authResult);
156 }
157
158 /**
159 * Ensures the authentication object in the secure context is set to null when authentication fails.
160 *
161 */
162 protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
163 SecureContext sc = SecureContextUtils.getSecureContext();
164
165 sc.setAuthentication(null);
166 ContextHolder.setContext(sc);
167
168 if (logger.isDebugEnabled()) {
169 logger.debug("Updated ContextHolder to contain null Authentication");
170 }
171
172 request.getSession().setAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY, failed);
173 }
174
175 public void init(FilterConfig filterConfig) throws ServletException { }
176
177 public void destroy() { }
178
179 }