⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tokenbasedremembermeservices.java

📁 acegi构造安全的java系统
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/* 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.rememberme;import java.util.Date;import java.util.Map;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.acegisecurity.Authentication;import org.acegisecurity.providers.rememberme.RememberMeAuthenticationToken;import org.acegisecurity.ui.AccessDeniedHandler;import org.acegisecurity.ui.AuthenticationDetailsSource;import org.acegisecurity.ui.AuthenticationDetailsSourceImpl;import org.acegisecurity.ui.logout.LogoutHandler;import org.acegisecurity.userdetails.UserDetails;import org.acegisecurity.userdetails.UserDetailsService;import org.acegisecurity.userdetails.UsernameNotFoundException;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.ApplicationContext;import org.springframework.util.Assert;import org.springframework.util.StringUtils;import org.springframework.web.bind.RequestUtils;/** * Identifies previously remembered users by a Base-64 encoded cookie. *  * <p> * This implementation does not rely on an external database, so is attractive * for simple applications. The cookie will be valid for a specific period from * the date of the last * {@link #loginSuccess(HttpServletRequest, HttpServletResponse, Authentication)}. * As per the interface contract, this method will only be called when the * principal completes a successful interactive authentication. As such the time * period commences from the last authentication attempt where they furnished * credentials - not the time period they last logged in via remember-me. The * implementation will only send a remember-me token if the parameter defined by * {@link #setParameter(String)} is present. * </p> *  * <p> * An {@link org.acegisecurity.userdetails.UserDetailsService} is required by * this implementation, so that it can construct a valid * <code>Authentication</code> from the returned {@link * org.acegisecurity.userdetails.UserDetails}. This is also necessary so that * the user's password is available and can be checked as part of the encoded * cookie. * </p> *  * <p> * The cookie encoded by this implementation adopts the following form: *  * <pre> * username + &quot;:&quot; + expiryTime + &quot;:&quot; + Md5Hex(username + &quot;:&quot; + expiryTime + &quot;:&quot; + password + &quot;:&quot; + key) * </pre> *  * </p> * <p> * As such, if the user changes their password any remember-me token will be * invalidated. Equally, the system administrator may invalidate every * remember-me token on issue by changing the key. This provides some reasonable * approaches to recovering from a remember-me token being left on a public * machine (eg kiosk system, Internet cafe etc). Most importantly, at no time is * the user's password ever sent to the user agent, providing an important * security safeguard. Unfortunately the username is necessary in this * implementation (as we do not want to rely on a database for remember-me * services) and as such high security applications should be aware of this * occasionally undesired disclosure of a valid username. * </p> * <p> * This is a basic remember-me implementation which is suitable for many * applications. However, we recommend a database-based implementation if you * require a more secure remember-me approach. * </p> * <p> * By default the tokens will be valid for 14 days from the last successful * authentication attempt. This can be changed using * {@link #setTokenValiditySeconds(long)}. * </p> *  * @author Ben Alex * @version $Id: TokenBasedRememberMeServices.java 1871 2007-05-25 03:12:49Z * benalex $ */public class TokenBasedRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler {	// ~ Static fields/initializers	// =====================================================================================	public static final String ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY = "ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE";	public static final String DEFAULT_PARAMETER = "_acegi_security_remember_me";	protected static final Log logger = LogFactory.getLog(TokenBasedRememberMeServices.class);	// ~ Instance fields	// ================================================================================================	protected AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();	private String key;	private String parameter = DEFAULT_PARAMETER;	private UserDetailsService userDetailsService;	protected long tokenValiditySeconds = 1209600; // 14 days	private boolean alwaysRemember = false;	private static final int DEFAULT_ORDER = Integer.MAX_VALUE; // ~ default	private String cookieName = ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY;	// ~ Methods	// ========================================================================================================	public void afterPropertiesSet() throws Exception {		Assert.hasLength(key);		Assert.hasLength(parameter);		Assert.hasLength(cookieName);		Assert.notNull(userDetailsService);	}	/**	 * Introspects the <code>Applicationcontext</code> for the single instance	 * of {@link AccessDeniedHandler}. If found invoke	 * setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) method by	 * providing the found instance of accessDeniedHandler as a method	 * parameter. If more than one instance of <code>AccessDeniedHandler</code>	 * is found, the method throws <code>IllegalStateException</code>.	 * 	 * @param applicationContext to locate the instance	 */	private void autoDetectAndUseAnyUserDetailsService(ApplicationContext applicationContext) {		Map map = applicationContext.getBeansOfType(UserDetailsService.class);		if (map.size() > 1) {			throw new IllegalArgumentException(					"More than one UserDetailsService beans detected please refer to the one using "							+ " [ principalRepositoryBeanRef  ] " + "attribute");		}		else if (map.size() == 1) {			setUserDetailsService((UserDetailsService) map.values().iterator().next());		}	}	public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {		Cookie[] cookies = request.getCookies();		if ((cookies == null) || (cookies.length == 0)) {			return null;		}		for (int i = 0; i < cookies.length; i++) {			if (cookieName.equals(cookies[i].getName())) {				String cookieValue = cookies[i].getValue();				for (int j = 0; j < cookieValue.length() % 4; j++) {					cookieValue = cookieValue + "=";				}				if (Base64.isArrayByteBase64(cookieValue.getBytes())) {					if (logger.isDebugEnabled()) {						logger.debug("Remember-me cookie detected");					}					// Decode token from Base64					// format of token is:					// username + ":" + expiryTime + ":" +					// Md5Hex(username + ":" + expiryTime + ":" + password + ":"					// + key)					String cookieAsPlainText = new String(Base64.decodeBase64(cookieValue.getBytes()));					String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":");					if (cookieTokens.length == 3) {						long tokenExpiryTime;						try {							tokenExpiryTime = new Long(cookieTokens[1]).longValue();						}						catch (NumberFormatException nfe) {							cancelCookie(request, response,									"Cookie token[1] did not contain a valid number (contained '" + cookieTokens[1]											+ "')");							return null;						}						if (isTokenExpired(tokenExpiryTime)) {							cancelCookie(request, response, "Cookie token[1] has expired (expired on '"									+ new Date(tokenExpiryTime) + "'; current time is '" + new Date() + "')");							return null;						}						// Check the user exists						// Defer lookup until after expiry time checked, to						// possibly avoid expensive lookup						UserDetails userDetails = loadUserDetails(request, response, cookieTokens);						if (userDetails == null) {							cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]									+ "' but was not found");							return null;						}						if (!isValidUserDetails(request, response, userDetails, cookieTokens)) {							return null;						}						// Check signature of token matches remaining details						// Must do this after user lookup, as we need the						// DAO-derived password						// If efficiency was a major issue, just add in a						// UserCache implementation,						// but recall this method is usually only called one per						// HttpSession						// (as if the token is valid, it will cause						// SecurityContextHolder population, whilst						// if invalid, will cause the cookie to be cancelled)						String expectedTokenSignature = makeTokenSignature(tokenExpiryTime, userDetails);						if (!expectedTokenSignature.equals(cookieTokens[2])) {							cancelCookie(request, response, "Cookie token[2] contained signature '" + cookieTokens[2]									+ "' but expected '" + expectedTokenSignature + "'");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -