All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider Maven / Gradle / Ivy

There is a newer version: 1.0.7
Show newest version
/* Copyright 2004, 2005 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.providers.dao;

import org.acegisecurity.AccountExpiredException;
import org.acegisecurity.AcegiMessageSource;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.CredentialsExpiredException;
import org.acegisecurity.DisabledException;
import org.acegisecurity.LockedException;
import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.providers.AuthenticationProvider;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.providers.dao.cache.NullUserCache;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UserDetailsService;
import org.acegisecurity.userdetails.UsernameNotFoundException;
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;


/**
 * A base {@link AuthenticationProvider} that allows subclasses to override and
 * work with {@link org.acegisecurity.userdetails.UserDetails} objects. The class is
 * designed to respond to {@link UsernamePasswordAuthenticationToken}
 * authentication requests.
 * 
 * 

* Upon successful validation, a * UsernamePasswordAuthenticationToken will be created and * returned to the caller. The token will include as its principal either a * String representation of the username, or the {@link * UserDetails} that was returned from the authentication repository. Using * String is appropriate if a container adapter is being used, as * it expects String representations of the username. Using * UserDetails is appropriate if you require access to additional * properties of the authenticated user, such as email addresses, * human-friendly names etc. As container adapters are not recommended to be * used, and UserDetails implementations provide additional * flexibility, by default a UserDetails is returned. To override * this default, set the {@link #setForcePrincipalAsString} to * true. *

* *

* Caching is handled via the UserDetails object being placed in * the {@link UserCache}. This ensures that subsequent requests with the same * username can be validated without needing to query the {@link * UserDetailsService}. It should be noted that if a user appears to present an * incorrect password, the {@link UserDetailsService} will be queried to * confirm the most up-to-date password was used for comparison. *

* * @author Ben Alex * @version $Id: AbstractUserDetailsAuthenticationProvider.java,v 1.7 2005/12/22 01:30:53 luke_t Exp $ */ public abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware { //~ Instance fields ======================================================== protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor(); private UserCache userCache = new NullUserCache(); private boolean forcePrincipalAsString = false; protected boolean hideUserNotFoundExceptions = true; //~ Methods ================================================================ /** * Allows subclasses to perform any additional checks of a returned (or * cached) UserDetails for a given authentication request. * Generally a subclass will at least compare the {@link * Authentication#getCredentials()} with a {@link * UserDetails#getPassword()}. If custom logic is needed to compare * additional properties of UserDetails and/or * UsernamePasswordAuthenticationToken, these should also * appear in this method. * * @param userDetails as retrieved from the {@link #retrieveUser(String, * UsernamePasswordAuthenticationToken)} or UserCache * @param authentication the current request that needs to be authenticated * * @throws AuthenticationException AuthenticationException if the * credentials could not be validated (generally a * BadCredentialsException, an * AuthenticationServiceException) */ protected abstract void additionalAuthenticationChecks( UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException; public final void afterPropertiesSet() throws Exception { Assert.notNull(this.userCache, "A user cache must be set"); Assert.notNull(this.messages, "A message source must be set"); doAfterPropertiesSet(); } public Authentication authenticate(Authentication authentication) throws AuthenticationException { Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, messages.getMessage( "AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported")); // Determine username String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName(); boolean cacheWasUsed = true; UserDetails user = this.userCache.getUserFromCache(username); if (user == null) { cacheWasUsed = false; try { user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); } catch (UsernameNotFoundException notFound) { if (hideUserNotFoundExceptions) { throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } else { throw notFound; } } Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract"); } if (!user.isAccountNonLocked()) { throw new LockedException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.locked", "User account is locked")); } if (!user.isEnabled()) { throw new DisabledException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.disabled", "User is disabled")); } if (!user.isAccountNonExpired()) { throw new AccountExpiredException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.expired", "User account has expired")); } // This check must come here, as we don't want to tell users // about account status unless they presented the correct credentials try { additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } catch (AuthenticationException exception) { // There was a problem, so try again after checking we're using latest data cacheWasUsed = false; user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } if (!user.isCredentialsNonExpired()) { throw new CredentialsExpiredException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.credentialsExpired", "User credentials have expired")); } if (!cacheWasUsed) { this.userCache.putUserInCache(user); } Object principalToReturn = user; if (forcePrincipalAsString) { principalToReturn = user.getUsername(); } return createSuccessAuthentication(principalToReturn, authentication, user); } /** * Creates a successful {@link Authentication} object. * *

* Protected so subclasses can override. *

* *

* Subclasses will usually store the original credentials the user supplied * (not salted or encoded passwords) in the returned * Authentication object. *

* * @param principal that should be the principal in the returned object * (defined by the {@link #isForcePrincipalAsString()} method) * @param authentication that was presented to the * provider for validation * @param user that was loaded by the implementation * * @return the successful authentication token */ protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) { // Ensure we return the original credentials the user supplied, // so subsequent attempts are successful even with encoded passwords. // Also ensure we return the original getDetails(), so that future // authentication events after cache expiry contain the details UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal, authentication.getCredentials(), user.getAuthorities()); result.setDetails(authentication.getDetails()); return result; } protected void doAfterPropertiesSet() throws Exception {} public UserCache getUserCache() { return userCache; } public boolean isForcePrincipalAsString() { return forcePrincipalAsString; } /** * Allows subclasses to actually retrieve the UserDetails from * an implementation-specific location, with the option of throwing an * AuthenticationException immediately if the presented * credentials are incorrect (this is especially useful if it is necessary * to bind to a resource as the user in order to obtain or generate a * UserDetails). * *

* Subclasses are not required to perform any caching, as the * AbstractUserDetailsAuthenticationProvider will by default * cache the UserDetails. The caching of * UserDetails does present additional complexity as this * means subsequent requests that rely on the cache will need to still * have their credentials validated, even if the correctness of * credentials was assured by subclasses adopting a binding-based strategy * in this method. Accordingly it is important that subclasses either * disable caching (if they want to ensure that this method is the only * method that is capable of authenticating a request, as no * UserDetails will ever be cached) or ensure subclasses * implement {@link #additionalAuthenticationChecks(UserDetails, * UsernamePasswordAuthenticationToken)} to compare the credentials of a * cached UserDetails with subsequent authentication * requests. *

* *

* Most of the time subclasses will not perform credentials inspection in * this method, instead performing it in {@link * #additionalAuthenticationChecks(UserDetails, * UsernamePasswordAuthenticationToken)} so that code related to * credentials validation need not be duplicated across two methods. *

* * @param username The username to retrieve * @param authentication The authentication request, which subclasses * may need to perform a binding-based retrieval of the * UserDetails * * @return the user information (never null - instead an * exception should the thrown) * * @throws AuthenticationException if the credentials could not be * validated (generally a BadCredentialsException, an * AuthenticationServiceException or * UsernameNotFoundException) */ protected abstract UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException; public void setForcePrincipalAsString(boolean forcePrincipalAsString) { this.forcePrincipalAsString = forcePrincipalAsString; } public void setMessageSource(MessageSource messageSource) { this.messages = new MessageSourceAccessor(messageSource); } public void setUserCache(UserCache userCache) { this.userCache = userCache; } public boolean supports(Class authentication) { return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)); } public boolean isHideUserNotFoundExceptions() { return hideUserNotFoundExceptions; } /** * By default the AbstractUserDetailsAuthenticationProvider * throws a BadCredentialsException if a username is * not found or the password is incorrect. Setting this property to * false will cause * UsernameNotFoundExceptions to be thrown instead for * the former. Note this is considered less secure than throwing * BadCredentialsException for both exceptions. * * @param hideUserNotFoundExceptions set to false if you * wish UsernameNotFoundExceptions to be thrown * instead of the non-specific * BadCredentialsException (defaults to * true) */ public void setHideUserNotFoundExceptions( boolean hideUserNotFoundExceptions) { this.hideUserNotFoundExceptions = hideUserNotFoundExceptions; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy