📄 digestprocessingfilter.java
字号:
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.acegisecurity.ui.digestauth;import org.acegisecurity.AcegiMessageSource;import org.acegisecurity.AuthenticationException;import org.acegisecurity.AuthenticationServiceException;import org.acegisecurity.BadCredentialsException;import org.acegisecurity.context.SecurityContextHolder;import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;import org.acegisecurity.providers.dao.UserCache;import org.acegisecurity.providers.dao.cache.NullUserCache;import org.acegisecurity.ui.AuthenticationDetailsSource;import org.acegisecurity.ui.AuthenticationDetailsSourceImpl;import org.acegisecurity.userdetails.UserDetails;import org.acegisecurity.userdetails.UserDetailsService;import org.acegisecurity.userdetails.UsernameNotFoundException;import org.acegisecurity.util.StringSplitUtils;import org.apache.commons.codec.binary.Base64;import org.apache.commons.codec.digest.DigestUtils;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.beans.factory.InitializingBean;import org.springframework.context.MessageSource;import org.springframework.context.MessageSourceAware;import org.springframework.context.support.MessageSourceAccessor;import org.springframework.util.Assert;import org.springframework.util.StringUtils;import java.io.IOException;import java.util.Map;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * Processes a HTTP request's Digest authorization headers, putting the result into the * <code>SecurityContextHolder</code>.<p>For a detailed background on what this filter is designed to process, * refer to <a href="http://www.ietf.org/rfc/rfc2617.txt">RFC 2617</a> (which superseded RFC 2069, although this * filter support clients that implement either RFC 2617 or RFC 2069).</p> * <p>This filter can be used to provide Digest authentication services to both remoting protocol clients (such as * Hessian and SOAP) as well as standard user agents (such as Internet Explorer and FireFox).</p> * <p>This Digest implementation has been designed to avoid needing to store session state between invocations. * All session management information is stored in the "nonce" that is sent to the client by the {@link * DigestProcessingFilterEntryPoint}.</p> * <P>If authentication is successful, the resulting {@link org.acegisecurity.Authentication Authentication} * object will be placed into the <code>SecurityContextHolder</code>.</p> * <p>If authentication fails, an {@link org.acegisecurity.ui.AuthenticationEntryPoint AuthenticationEntryPoint} * implementation is called. This must always be {@link DigestProcessingFilterEntryPoint}, which will prompt the user * to authenticate again via Digest authentication.</p> * <p>Note there are limitations to Digest authentication, although it is a more comprehensive and secure solution * than Basic authentication. Please see RFC 2617 section 4 for a full discussion on the advantages of Digest * authentication over Basic authentication, including commentary on the limitations that it still imposes.</p> * <p><b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the {@link * org.acegisecurity.util.FilterToBeanProxy}.</p> */public class DigestProcessingFilter implements Filter, InitializingBean, MessageSourceAware { //~ Static fields/initializers ===================================================================================== private static final Log logger = LogFactory.getLog(DigestProcessingFilter.class); //~ Instance fields ================================================================================================ private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl(); private DigestProcessingFilterEntryPoint authenticationEntryPoint; protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor(); private UserCache userCache = new NullUserCache(); private UserDetailsService userDetailsService; private boolean passwordAlreadyEncoded = false; //~ Methods ======================================================================================================== public void afterPropertiesSet() throws Exception { Assert.notNull(userDetailsService, "A UserDetailsService is required"); Assert.notNull(authenticationEntryPoint, "A DigestProcessingFilterEntryPoint is required"); } public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (!(request instanceof HttpServletRequest)) { throw new ServletException("Can only process HttpServletRequest"); } if (!(response instanceof HttpServletResponse)) { throw new ServletException("Can only process HttpServletResponse"); } HttpServletRequest httpRequest = (HttpServletRequest) request; String header = httpRequest.getHeader("Authorization"); if (logger.isDebugEnabled()) { logger.debug("Authorization header received from user agent: " + header); } if ((header != null) && header.startsWith("Digest ")) { String section212response = header.substring(7); String[] headerEntries = StringSplitUtils.splitIgnoringQuotes(section212response, ','); Map headerMap = StringSplitUtils.splitEachArrayElementAndCreateMap(headerEntries, "=", "\""); String username = (String) headerMap.get("username"); String realm = (String) headerMap.get("realm"); String nonce = (String) headerMap.get("nonce"); String uri = (String) headerMap.get("uri"); String responseDigest = (String) headerMap.get("response"); String qop = (String) headerMap.get("qop"); // RFC 2617 extension String nc = (String) headerMap.get("nc"); // RFC 2617 extension String cnonce = (String) headerMap.get("cnonce"); // RFC 2617 extension // Check all required parameters were supplied (ie RFC 2069) if ((username == null) || (realm == null) || (nonce == null) || (uri == null) || (response == null)) { if (logger.isDebugEnabled()) { logger.debug("extracted username: '" + username + "'; realm: '" + username + "'; nonce: '" + username + "'; uri: '" + username + "'; response: '" + username + "'"); } fail(request, response, new BadCredentialsException(messages.getMessage("DigestProcessingFilter.missingMandatory", new Object[]{section212response}, "Missing mandatory digest value; received header {0}"))); return; } // Check all required parameters for an "auth" qop were supplied (ie RFC 2617) if ("auth".equals(qop)) { if ((nc == null) || (cnonce == null)) { if (logger.isDebugEnabled()) { logger.debug("extracted nc: '" + nc + "'; cnonce: '" + cnonce + "'"); } fail(request, response, new BadCredentialsException(messages.getMessage("DigestProcessingFilter.missingAuth", new Object[]{section212response}, "Missing mandatory digest value; received header {0}"))); return; } } // Check realm name equals what we expected if (!this.getAuthenticationEntryPoint().getRealmName().equals(realm)) { fail(request, response, new BadCredentialsException(messages.getMessage("DigestProcessingFilter.incorrectRealm", new Object[]{realm, this.getAuthenticationEntryPoint().getRealmName()}, "Response realm name '{0}' does not match system realm name of '{1}'"))); return; } // Check nonce was a Base64 encoded (as sent by DigestProcessingFilterEntryPoint) if (!Base64.isArrayByteBase64(nonce.getBytes())) { fail(request, response, new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceEncoding", new Object[]{nonce}, "Nonce is not encoded in Base64; received nonce {0}"))); return; } // Decode nonce from Base64 // format of nonce is: // base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key)) String nonceAsPlainText = new String(Base64.decodeBase64(nonce.getBytes())); String[] nonceTokens = StringUtils.delimitedListToStringArray(nonceAsPlainText, ":"); if (nonceTokens.length != 2) { fail(request, response, new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceNotTwoTokens", new Object[]{nonceAsPlainText}, "Nonce should have yielded two tokens but was {0}"))); return; } // Extract expiry time from nonce long nonceExpiryTime; try { nonceExpiryTime = new Long(nonceTokens[0]).longValue(); } catch (NumberFormatException nfe) { fail(request, response, new BadCredentialsException(messages.getMessage("DigestProcessingFilter.nonceNotNumeric", new Object[]{nonceAsPlainText}, "Nonce token should have yielded a numeric first token, but was {0}"))); return; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -