1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.acegisecurity.ui.basicauth;
17
18 import org.acegisecurity.Authentication;
19 import org.acegisecurity.AuthenticationException;
20 import org.acegisecurity.AuthenticationManager;
21 import org.acegisecurity.context.SecurityContextHolder;
22 import org.acegisecurity.intercept.web.AuthenticationEntryPoint;
23 import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
24 import org.acegisecurity.ui.WebAuthenticationDetails;
25
26 import org.apache.commons.codec.binary.Base64;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 import org.springframework.beans.factory.InitializingBean;
31
32 import org.springframework.util.Assert;
33
34 import java.io.IOException;
35
36 import javax.servlet.Filter;
37 import javax.servlet.FilterChain;
38 import javax.servlet.FilterConfig;
39 import javax.servlet.ServletException;
40 import javax.servlet.ServletRequest;
41 import javax.servlet.ServletResponse;
42 import javax.servlet.http.HttpServletRequest;
43 import javax.servlet.http.HttpServletResponse;
44
45
46 /***
47 * Processes a HTTP request's BASIC authorization headers, putting the result
48 * into the <code>SecurityContextHolder</code>.
49 *
50 * <p>
51 * For a detailed background on what this filter is designed to process, refer
52 * to <A HREF="http://www.faqs.org/rfcs/rfc1945.html">RFC 1945, Section
53 * 11.1</A>. Any realm name presented in the HTTP request is ignored.
54 * </p>
55 *
56 * <p>
57 * In summary, this filter is responsible for processing any request that has a
58 * HTTP request header of <code>Authorization</code> with an authentication
59 * scheme of <code>Basic</code> and a Base64-encoded
60 * <code>username:password</code> token. For example, to authenticate user
61 * "Aladdin" with password "open sesame" the following header would be
62 * presented:
63 * </p>
64 *
65 * <p>
66 * <code>Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==</code>.
67 * </p>
68 *
69 * <p>
70 * This filter can be used to provide BASIC authentication services to both
71 * remoting protocol clients (such as Hessian and SOAP) as well as standard
72 * user agents (such as Internet Explorer and Netscape).
73 * </p>
74 *
75 * <P>
76 * If authentication is successful, the resulting {@link Authentication} object
77 * will be placed into the <code>SecurityContextHolder</code>.
78 * </p>
79 *
80 * <p>
81 * If authentication fails, an {@link AuthenticationEntryPoint} implementation
82 * is called. Usually this should be {@link BasicProcessingFilterEntryPoint},
83 * which will prompt the user to authenticate again via BASIC authentication.
84 * </p>
85 *
86 * <p>
87 * Basic authentication is an attractive protocol because it is simple and
88 * widely deployed. However, it still transmits a password in clear text and
89 * as such is undesirable in many situations. Digest authentication is also
90 * provided by Acegi Security and should be used instead of Basic
91 * authentication wherever possible. See {@link
92 * org.acegisecurity.ui.digestauth.DigestProcessingFilter}.
93 * </p>
94 *
95 * <p>
96 * <b>Do not use this class directly.</b> Instead configure
97 * <code>web.xml</code> to use the {@link
98 * org.acegisecurity.util.FilterToBeanProxy}.
99 * </p>
100 *
101 * @author Ben Alex
102 * @version $Id: BasicProcessingFilter.java,v 1.16 2005/11/17 00:56:48 benalex Exp $
103 */
104 public class BasicProcessingFilter implements Filter, InitializingBean {
105
106
107 private static final Log logger = LogFactory.getLog(BasicProcessingFilter.class);
108
109
110
111 private AuthenticationEntryPoint authenticationEntryPoint;
112 private AuthenticationManager authenticationManager;
113
114
115
116 public void setAuthenticationEntryPoint(
117 AuthenticationEntryPoint authenticationEntryPoint) {
118 this.authenticationEntryPoint = authenticationEntryPoint;
119 }
120
121 public AuthenticationEntryPoint getAuthenticationEntryPoint() {
122 return authenticationEntryPoint;
123 }
124
125 public void setAuthenticationManager(
126 AuthenticationManager authenticationManager) {
127 this.authenticationManager = authenticationManager;
128 }
129
130 public AuthenticationManager getAuthenticationManager() {
131 return authenticationManager;
132 }
133
134 public void afterPropertiesSet() throws Exception {
135 Assert.notNull(this.authenticationManager,
136 "An AuthenticationManager is required");
137 Assert.notNull(this.authenticationEntryPoint,
138 "An AuthenticationEntryPoint is required");
139 }
140
141 public void destroy() {}
142
143 public void doFilter(ServletRequest request, ServletResponse response,
144 FilterChain chain) throws IOException, ServletException {
145 if (!(request instanceof HttpServletRequest)) {
146 throw new ServletException("Can only process HttpServletRequest");
147 }
148
149 if (!(response instanceof HttpServletResponse)) {
150 throw new ServletException("Can only process HttpServletResponse");
151 }
152
153 HttpServletRequest httpRequest = (HttpServletRequest) request;
154
155 String header = httpRequest.getHeader("Authorization");
156
157 if (logger.isDebugEnabled()) {
158 logger.debug("Authorization header: " + header);
159 }
160
161 if ((header != null) && header.startsWith("Basic ")) {
162 String base64Token = header.substring(6);
163 String token = new String(Base64.decodeBase64(
164 base64Token.getBytes()));
165
166 String username = "";
167 String password = "";
168 int delim = token.indexOf(":");
169
170 if (delim != -1) {
171 username = token.substring(0, delim);
172 password = token.substring(delim + 1);
173 }
174
175
176 Authentication existingAuth = SecurityContextHolder.getContext()
177 .getAuthentication();
178
179 if ((existingAuth == null)
180 || !existingAuth.getName().equals(username)
181 || !existingAuth.isAuthenticated()) {
182 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,
183 password);
184 authRequest.setDetails(new WebAuthenticationDetails(
185 httpRequest, false));
186
187 Authentication authResult;
188
189 try {
190 authResult = authenticationManager.authenticate(authRequest);
191 } catch (AuthenticationException failed) {
192
193 if (logger.isDebugEnabled()) {
194 logger.debug("Authentication request for user: "
195 + username + " failed: " + failed.toString());
196 }
197
198 SecurityContextHolder.getContext().setAuthentication(null);
199 authenticationEntryPoint.commence(request, response, failed);
200
201 return;
202 }
203
204
205 if (logger.isDebugEnabled()) {
206 logger.debug("Authentication success: "
207 + authResult.toString());
208 }
209
210 SecurityContextHolder.getContext().setAuthentication(authResult);
211 }
212 }
213
214 chain.doFilter(request, response);
215 }
216
217 public void init(FilterConfig arg0) throws ServletException {}
218 }