Make User available from Authentication via DaoAuthenticationProvider.
This commit is contained in:
parent
36ad7f3963
commit
1a0bec5bf1
|
@ -1,3 +1,10 @@
|
|||
Changes in version 0.6 (2004-xx-xx)
|
||||
-----------------------------------
|
||||
|
||||
* Added feature so DaoAuthenticationProvider returns User in Authentication
|
||||
* Fixed Linux compatibility issues (directory case sensitivity etc)
|
||||
* Documentation improvements
|
||||
|
||||
Changes in version 0.51 (2004-06-06)
|
||||
------------------------------------
|
||||
|
||||
|
|
|
@ -51,12 +51,26 @@ import org.springframework.dao.DataAccessException;
|
|||
* <p>
|
||||
* Upon successful validation, a
|
||||
* <code>UsernamePasswordAuthenticationToken</code> will be created and
|
||||
* returned to the caller. In addition, the {@link User} will be placed in the
|
||||
* {@link UserCache} so that subsequent requests with the same username can be
|
||||
* validated without needing to query the {@link AuthenticationDao}. It should
|
||||
* be noted that if a user appears to present an incorrect password, the
|
||||
* {@link AuthenticationDao} will be queried to confirm the most up-to-date
|
||||
* password was used for comparison.
|
||||
* returned to the caller. The token will include as its principal either a
|
||||
* <code>String</code> representation of the username, or the {@link User}
|
||||
* that was returned from the authentication repository. Using
|
||||
* <code>String</code> is appropriate if a container adapter is being used, as
|
||||
* it expects <code>String</code> representations of the username. Using
|
||||
* <code>User</code> 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 <code>User</code> provides additional flexibility, by default a
|
||||
* <code>User</code> is returned. To override this default, set the {@link
|
||||
* #setForcePrincipalAsString} to <code>true</code>.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* Caching is handled via the <code>User</code> 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
|
||||
* AuthenticationDao}. It should be noted that if a user appears to present an
|
||||
* incorrect password, the {@link AuthenticationDao} will be queried to
|
||||
* confirm the most up-to-date password was used for comparison.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
|
@ -79,6 +93,7 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
|
|||
private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
|
||||
private SaltSource saltSource;
|
||||
private UserCache userCache = new NullUserCache();
|
||||
private boolean forcePrincipalAsString = false;
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
|
@ -95,6 +110,14 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
|
|||
return authenticationDao;
|
||||
}
|
||||
|
||||
public void setForcePrincipalAsString(boolean forcePrincipalAsString) {
|
||||
this.forcePrincipalAsString = forcePrincipalAsString;
|
||||
}
|
||||
|
||||
public boolean isForcePrincipalAsString() {
|
||||
return forcePrincipalAsString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the PasswordEncoder instance to be used to encode and validate
|
||||
* passwords. If not set, {@link PlaintextPasswordEncoder} will be used by
|
||||
|
@ -148,13 +171,19 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
|
|||
|
||||
public Authentication authenticate(Authentication authentication)
|
||||
throws AuthenticationException {
|
||||
// Determine username
|
||||
String username = authentication.getPrincipal().toString();
|
||||
|
||||
if (authentication.getPrincipal() instanceof User) {
|
||||
username = ((User) authentication.getPrincipal()).getUsername();
|
||||
}
|
||||
|
||||
boolean cacheWasUsed = true;
|
||||
User user = this.userCache.getUserFromCache(authentication.getPrincipal()
|
||||
.toString());
|
||||
User user = this.userCache.getUserFromCache(username);
|
||||
|
||||
if (user == null) {
|
||||
cacheWasUsed = false;
|
||||
user = getUserFromBackend(authentication);
|
||||
user = getUserFromBackend(username);
|
||||
}
|
||||
|
||||
if (!user.isEnabled()) {
|
||||
|
@ -170,7 +199,7 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
|
|||
// Password incorrect, so ensure we're using most current password
|
||||
if (cacheWasUsed) {
|
||||
cacheWasUsed = false;
|
||||
user = getUserFromBackend(authentication);
|
||||
user = getUserFromBackend(username);
|
||||
}
|
||||
|
||||
if (!isPasswordCorrect(authentication, user)) {
|
||||
|
@ -194,9 +223,15 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
|
|||
}
|
||||
}
|
||||
|
||||
Object principalToReturn = user;
|
||||
|
||||
if (forcePrincipalAsString) {
|
||||
principalToReturn = user.getUsername();
|
||||
}
|
||||
|
||||
// Ensure we return the original credentials the user supplied,
|
||||
// so subsequent attempts are successful even with encoded passwords
|
||||
return new UsernamePasswordAuthenticationToken(user.getUsername(),
|
||||
return new UsernamePasswordAuthenticationToken(principalToReturn,
|
||||
authentication.getCredentials(), user.getAuthorities());
|
||||
}
|
||||
|
||||
|
@ -220,10 +255,9 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
|
|||
authentication.getCredentials().toString(), salt);
|
||||
}
|
||||
|
||||
private User getUserFromBackend(Authentication authentication) {
|
||||
private User getUserFromBackend(String username) {
|
||||
try {
|
||||
return this.authenticationDao.loadUserByUsername(authentication.getPrincipal()
|
||||
.toString());
|
||||
return this.authenticationDao.loadUserByUsername(username);
|
||||
} catch (UsernameNotFoundException notFound) {
|
||||
throw new BadCredentialsException("Bad credentials presented");
|
||||
} catch (DataAccessException repositoryProblem) {
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
<!-- Authentication provider that queries our data access object -->
|
||||
<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
|
||||
<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
|
||||
<property name="forcePrincipalAsString"><value>true</value></property>
|
||||
</bean>
|
||||
|
||||
<!-- The authentication manager that iterates through our only authentication provider -->
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
<!-- Authentication provider that queries our data access object -->
|
||||
<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
|
||||
<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
|
||||
<property name="forcePrincipalAsString"><value>true</value></property>
|
||||
</bean>
|
||||
|
||||
<!-- The authentication manager that iterates through our only authentication provider -->
|
||||
|
|
|
@ -166,7 +166,7 @@ public class DaoAuthenticationProviderTests extends TestCase {
|
|||
}
|
||||
|
||||
UsernamePasswordAuthenticationToken castResult = (UsernamePasswordAuthenticationToken) result;
|
||||
assertEquals("marissa", castResult.getPrincipal());
|
||||
assertEquals(User.class, castResult.getPrincipal().getClass());
|
||||
assertEquals("koala", castResult.getCredentials());
|
||||
assertEquals("ROLE_ONE", castResult.getAuthorities()[0].getAuthority());
|
||||
assertEquals("ROLE_TWO", castResult.getAuthorities()[1].getAuthority());
|
||||
|
@ -192,7 +192,7 @@ public class DaoAuthenticationProviderTests extends TestCase {
|
|||
}
|
||||
|
||||
UsernamePasswordAuthenticationToken castResult = (UsernamePasswordAuthenticationToken) result;
|
||||
assertEquals("marissa", castResult.getPrincipal());
|
||||
assertEquals(User.class, castResult.getPrincipal().getClass());
|
||||
|
||||
// We expect original credentials user submitted to be returned
|
||||
assertEquals("koala", castResult.getCredentials());
|
||||
|
|
|
@ -24,6 +24,7 @@ import net.sf.acegisecurity.MockFilterConfig;
|
|||
import net.sf.acegisecurity.MockHttpServletRequest;
|
||||
import net.sf.acegisecurity.MockHttpServletResponse;
|
||||
import net.sf.acegisecurity.MockHttpSession;
|
||||
import net.sf.acegisecurity.providers.dao.User;
|
||||
import net.sf.acegisecurity.ui.webapp.HttpSessionIntegrationFilter;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
@ -199,8 +200,8 @@ public class BasicProcessingFilterTests extends TestCase {
|
|||
|
||||
assertTrue(request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY) != null);
|
||||
assertEquals("marissa",
|
||||
((Authentication) request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY)).getPrincipal()
|
||||
.toString());
|
||||
((User) ((Authentication) request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY)).getPrincipal())
|
||||
.getUsername());
|
||||
}
|
||||
|
||||
public void testOtherAuthorizationSchemeIsIgnored()
|
||||
|
@ -291,8 +292,8 @@ public class BasicProcessingFilterTests extends TestCase {
|
|||
|
||||
assertTrue(request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY) != null);
|
||||
assertEquals("marissa",
|
||||
((Authentication) request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY)).getPrincipal()
|
||||
.toString());
|
||||
((User) ((Authentication) request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY)).getPrincipal())
|
||||
.getUsername());
|
||||
|
||||
// NOW PERFORM FAILED AUTHENTICATION
|
||||
// Setup our HTTP request
|
||||
|
|
|
@ -20,6 +20,8 @@ import junit.framework.TestCase;
|
|||
import net.sf.acegisecurity.MockHttpServletRequest;
|
||||
import net.sf.acegisecurity.MockHttpServletResponse;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link CasProcessingFilterEntryPoint}.
|
||||
|
@ -99,8 +101,11 @@ public class CasProcessingFilterEntryPointTests extends TestCase {
|
|||
|
||||
ep.afterPropertiesSet();
|
||||
ep.commence(request, response);
|
||||
assertEquals("https://cas/login?service=https://mycompany.com/bigWebApp/j_acegi_cas_security_check",
|
||||
response.getRedirect());
|
||||
|
||||
assertEquals("https://cas/login?service="
|
||||
+ URLEncoder.encode(
|
||||
"https://mycompany.com/bigWebApp/j_acegi_cas_security_check",
|
||||
"UTF-8"), response.getRedirect());
|
||||
}
|
||||
|
||||
public void testNormalOperationWithRenewTrue() throws Exception {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
<subtitle>Reference Documentation</subtitle>
|
||||
|
||||
<releaseinfo>0.51</releaseinfo>
|
||||
<releaseinfo>0.6</releaseinfo>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
|
@ -946,10 +946,24 @@
|
|||
increased the complexity of the <literal>AuthenticationDao</literal>
|
||||
interface. For instance, a method would be required to increase the
|
||||
count of unsuccessful authentication attempts. Such functionality
|
||||
could be easily provided in a new
|
||||
<literal>AuthenticationManager</literal> or
|
||||
<literal>AuthenticationProvider</literal> implementation if it were
|
||||
desired.</para>
|
||||
could be easily provided by leveraging the application event
|
||||
publishing features discussed below.</para>
|
||||
|
||||
<para><literal>DaoAuthenticationProvider</literal> returns an
|
||||
<literal>Authentication</literal> object which in turn has its
|
||||
<literal>principal</literal> property set. The principal will be
|
||||
either a <literal>String</literal> (which is essentially the username)
|
||||
or a <literal>User</literal> object (which was looked up from the
|
||||
<literal>AuthenticationDao</literal>). By default the
|
||||
<literal>User</literal> is returned, as this enables applications to
|
||||
subclass <literal>User</literal> and add extra properties potentially
|
||||
of use in applications, such as the user's full name, email address
|
||||
etc. If using container adapters, or if your applications were written
|
||||
to operate with <literal>String</literal>s (as was the case for
|
||||
releases prior to Acegi Security 0.6), you should set the
|
||||
<literal>DaoAuthenticationProvider.forcePrincipalAsString</literal>
|
||||
property to <literal>true</literal> in your application
|
||||
context.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="security-authentication-provider-events">
|
||||
|
@ -1927,6 +1941,11 @@ public boolean supports(Class clazz);</programlisting></para>
|
|||
provided below. Once installed, please take the time to try the sample
|
||||
application to ensure your container adapter is properly
|
||||
configured.</para>
|
||||
|
||||
<para>When using container adapters with the
|
||||
<literal>DaoAuthenticationProvider</literal>, ensure you set its
|
||||
<literal>forcePrincipalAsString</literal> property to
|
||||
<literal>true</literal>.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="security-container-adapters-catalina">
|
||||
|
@ -2497,7 +2516,7 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
|
|||
<literal>PasswordHandler</literal> will do).</para>
|
||||
|
||||
<para>To install, you will need to download and extract the CAS server
|
||||
archive. We used version 2.0.12 Beta 3. There will be a
|
||||
archive. We used version 2.0.12. There will be a
|
||||
<literal>/web</literal> directory in the root of the deployment. Copy
|
||||
an <literal>applicationContext.xml</literal> containing your
|
||||
<literal>AuthenticationManager</literal> as well as the
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
# $Id$
|
||||
|
||||
# Project version
|
||||
acegi-security-version=0.51
|
||||
acegi-security-version=0.6
|
||||
|
||||
# Project name
|
||||
name=acegi-security-system-for-spring
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
<!-- Authentication provider that queries our data access object -->
|
||||
<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
|
||||
<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
|
||||
<property name="forcePrincipalAsString"><value>true</value></property>
|
||||
</bean>
|
||||
|
||||
<!-- The authentication manager that iterates through our only authentication provider -->
|
||||
|
|
|
@ -19,6 +19,7 @@ import net.sf.acegisecurity.AccessDeniedException;
|
|||
import net.sf.acegisecurity.Authentication;
|
||||
import net.sf.acegisecurity.context.ContextHolder;
|
||||
import net.sf.acegisecurity.context.SecureContext;
|
||||
import net.sf.acegisecurity.providers.dao.User;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
|
@ -88,7 +89,13 @@ public class ContactManagerFacade implements ContactManager, InitializingBean {
|
|||
Authentication auth = ((SecureContext) ContextHolder.getContext())
|
||||
.getAuthentication();
|
||||
|
||||
if (auth.getPrincipal().toString().equals(result.getOwner())) {
|
||||
String username = auth.getPrincipal().toString();
|
||||
|
||||
if (auth.getPrincipal() instanceof User) {
|
||||
username = ((User) auth.getPrincipal()).getUsername();
|
||||
}
|
||||
|
||||
if (username.equals(result.getOwner())) {
|
||||
return result;
|
||||
} else {
|
||||
throw new AccessDeniedException(
|
||||
|
|
|
@ -18,6 +18,7 @@ package sample.contact;
|
|||
import net.sf.acegisecurity.Authentication;
|
||||
import net.sf.acegisecurity.ConfigAttribute;
|
||||
import net.sf.acegisecurity.ConfigAttributeDefinition;
|
||||
import net.sf.acegisecurity.providers.dao.User;
|
||||
import net.sf.acegisecurity.vote.AccessDecisionVoter;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
@ -96,9 +97,15 @@ public class ContactSecurityVoter implements AccessDecisionVoter {
|
|||
}
|
||||
|
||||
if (passedOwner != null) {
|
||||
String username = authentication.getPrincipal().toString();
|
||||
|
||||
if (authentication.getPrincipal() instanceof User) {
|
||||
username = ((User) authentication.getPrincipal())
|
||||
.getUsername();
|
||||
}
|
||||
|
||||
// Check the authentication principal matches the passed owner
|
||||
if (passedOwner.equals(authentication.getPrincipal()
|
||||
.toString())) {
|
||||
if (passedOwner.equals(username)) {
|
||||
return ACCESS_GRANTED;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import net.sf.acegisecurity.AuthenticationCredentialsNotFoundException;
|
|||
import net.sf.acegisecurity.GrantedAuthority;
|
||||
import net.sf.acegisecurity.context.ContextHolder;
|
||||
import net.sf.acegisecurity.context.SecureContext;
|
||||
import net.sf.acegisecurity.providers.dao.User;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
|
@ -74,10 +75,17 @@ public class SecureIndexController implements Controller, InitializingBean {
|
|||
+ "SecureContext");
|
||||
}
|
||||
|
||||
final Authentication currentUser = secureContext.getAuthentication();
|
||||
// Lookup username. As we must accommodate DaoAuthenticationProvider,
|
||||
// CAS and container based authentication, we take care with casting
|
||||
Authentication auth = secureContext.getAuthentication();
|
||||
String username = auth.getPrincipal().toString();
|
||||
|
||||
if (auth.getPrincipal() instanceof User) {
|
||||
username = ((User) auth.getPrincipal()).getUsername();
|
||||
}
|
||||
|
||||
boolean supervisor = false;
|
||||
GrantedAuthority[] granted = currentUser.getAuthorities();
|
||||
GrantedAuthority[] granted = auth.getAuthorities();
|
||||
|
||||
for (int i = 0; i < granted.length; i++) {
|
||||
if (granted[i].getAuthority().equals("ROLE_SUPERVISOR")) {
|
||||
|
@ -85,13 +93,12 @@ public class SecureIndexController implements Controller, InitializingBean {
|
|||
}
|
||||
}
|
||||
|
||||
Contact[] myContacts = contactManager.getAllByOwner(currentUser.getPrincipal()
|
||||
.toString());
|
||||
Contact[] myContacts = contactManager.getAllByOwner(username);
|
||||
|
||||
Map model = new HashMap();
|
||||
model.put("contacts", myContacts);
|
||||
model.put("supervisor", new Boolean(supervisor));
|
||||
model.put("user", currentUser.getPrincipal().toString());
|
||||
model.put("user", username);
|
||||
|
||||
return new ModelAndView("index", "model", model);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,10 @@
|
|||
|
||||
package sample.contact;
|
||||
|
||||
import net.sf.acegisecurity.Authentication;
|
||||
import net.sf.acegisecurity.context.ContextHolder;
|
||||
import net.sf.acegisecurity.context.SecureContext;
|
||||
import net.sf.acegisecurity.providers.dao.User;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.SimpleFormController;
|
||||
|
@ -54,8 +56,14 @@ public class WebContactAddController extends SimpleFormController {
|
|||
public ModelAndView onSubmit(Object command) throws ServletException {
|
||||
String name = ((WebContact) command).getName();
|
||||
String email = ((WebContact) command).getEmail();
|
||||
String owner = ((SecureContext) ContextHolder.getContext()).getAuthentication()
|
||||
.getPrincipal().toString();
|
||||
|
||||
Authentication auth = ((SecureContext) ContextHolder.getContext())
|
||||
.getAuthentication();
|
||||
String owner = auth.getPrincipal().toString();
|
||||
|
||||
if (auth.getPrincipal() instanceof User) {
|
||||
owner = ((User) auth.getPrincipal()).getUsername();
|
||||
}
|
||||
|
||||
Contact contact = new Contact(contactManager.getNextId(), name, email,
|
||||
owner);
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
===============================================================================
|
||||
ACEGI SECURITY SYSTEM FOR SPRING - UPGRADING FROM 0.5 TO 0.6
|
||||
===============================================================================
|
||||
|
||||
The following should help most casual users of the project update their
|
||||
applications:
|
||||
|
||||
- Locate and remove all property references to
|
||||
DaoAuthenticationProvider.key and
|
||||
DaoAuthenticationProvider.refreshTokenInterval.
|
||||
|
||||
- If you are using DaoAuthenticationProvider and either (i) you are using
|
||||
container adapters or (ii) your code relies on the Authentication object
|
||||
having its getPrincipal() return a String, you must set the new
|
||||
DaoAuthenticationProvider property, forcePrincipalAsString, to true.
|
||||
By default DaoAuthenticationProvider returns an Authentication object
|
||||
containing the relevant User, which allows access to additional properties.
|
||||
Where possible, we recommend you change your code to something like this,
|
||||
so that you can leave forcePrincipalAsString to the false default:
|
||||
|
||||
String username = authentication.getPrincipal();
|
||||
if (authentication.getPrincipal() instanceof User) {
|
||||
username = ((User) authentication.getPrincipal()).getUsername();
|
||||
}
|
||||
|
||||
|
||||
$Id$
|
Loading…
Reference in New Issue