Refactor Authentication.isAuthenticated() handling to be more performance (as per developer list discussion).

This commit is contained in:
Ben Alex 2005-06-22 06:30:46 +00:00
parent 1e12e51b9c
commit 5f75e9bf9a
16 changed files with 211 additions and 154 deletions

View File

@ -39,16 +39,55 @@ import java.security.Principal;
public interface Authentication extends Principal, Serializable {
//~ Methods ================================================================
public void setAuthenticated(boolean isAuthenticated);
/**
* See {@link #isAuthenticated()} for a full description.
*
* <p>
* Implementations should <b>always</b> allow this method to be called with
* a <code>false</code> parameter, as this is used by various classes to
* specify the authentication token should not be trusted. If an
* implementation wishes to reject an invocation with a <code>true</code>
* parameter (which would indicate the authentication token is trusted - a
* potential security risk) the implementation should throw an {@link
* IllegalArgumentException}.
* </p>
*
* @param isAuthenticated <code>true</code> if the token should be trusted
* (which may result in an exception) or <code>false</code> if the
* token should not be trusted
*
* @throws IllegalArgumentException if an attempt to make the
* authentication token trusted (by passing <code>true</code> as
* the argument) is rejected due to the implementation being
* immutable or implementing its own alternative approach to
* {@link #isAuthenticated()}
*/
public void setAuthenticated(boolean isAuthenticated)
throws IllegalArgumentException;
/**
* Indicates whether or not authentication was attempted by the {@link
* net.sf.acegisecurity.intercept.AbstractSecurityInterceptor}. Note that
* classes should not rely on this value as being valid unless it has been
* set by a trusted <code>AbstractSecurityInterceptor</code>.
* Used to indicate to <code>AbstractSecurityInterceptor</code> whether it
* should present the authentication token to the
* <code>AuthenticationManager</code>. Typically an
* <code>AuthenticationManager</code> (or, more often, one of its
* <code>AuthenticationProvider</code>s) will return an immutable
* authentication token after successful authentication, in which case
* that token can safely return <code>true</code> to this method.
* Returning <code>true</code> will improve performance, as calling the
* <code>AuthenticationManager</code> for every request will no longer be
* necessary.
*
* <p>
* For security reasons, implementations of this interface should be very
* careful about returning <code>true</code> to this method unless they
* are either immutable, or have some way of ensuring the properties have
* not been changed since original creation.
* </p>
*
* @return true if authenticated by the
* <code>AbstractSecurityInterceptor</code>
* @return true if the token has been authenticated and the
* <code>AbstractSecurityInterceptor</code> does not need to
* represent the token for re-authentication to the
* <code>AuthenticationManager</code>
*/
public boolean isAuthenticated();

View File

@ -82,6 +82,15 @@ public class ContextPropagatingRemoteInvocation extends RemoteInvocation {
/**
* Invoked on the server-side as described in the class JavaDocs.
*
* <p>
* Invocations will always have their {@link
* net.sf.acegisecurity.Authentication#setAuthenticated(boolean)} set to
* <code>false</code>, which is guaranteed to always be accepted by
* <code>Authentication</code> implementations. This ensures that even
* remotely authenticated <code>Authentication</code>s will be untrusted
* by the server-side, which is an appropriate security measure.
* </p>
*
* @param targetObject the target object to apply the invocation to
*
@ -97,6 +106,12 @@ public class ContextPropagatingRemoteInvocation extends RemoteInvocation {
InvocationTargetException {
SecurityContextHolder.setContext(securityContext);
if ((SecurityContextHolder.getContext() != null)
&& (SecurityContextHolder.getContext().getAuthentication() != null)) {
SecurityContextHolder.getContext().getAuthentication()
.setAuthenticated(false);
}
if (logger.isDebugEnabled()) {
logger.debug("Set SecurityContextHolder to contain: "
+ securityContext);

View File

@ -364,30 +364,42 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean,
object, attr);
}
// Attempt authentication
// Attempt authentication if not already authenticated
Authentication authenticated;
try {
authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext()
.getAuthentication());
} catch (AuthenticationException authenticationException) {
AuthenticationFailureEvent event = new AuthenticationFailureEvent(object,
attr,
SecurityContextHolder.getContext().getAuthentication(),
authenticationException);
this.context.publishEvent(event);
if (!SecurityContextHolder.getContext().getAuthentication()
.isAuthenticated()) {
try {
authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext()
.getAuthentication());
} catch (AuthenticationException authenticationException) {
AuthenticationFailureEvent event = new AuthenticationFailureEvent(object,
attr,
SecurityContextHolder.getContext()
.getAuthentication(),
authenticationException);
this.context.publishEvent(event);
throw authenticationException;
throw authenticationException;
}
// We don't authenticated.setAuthentication(true), because each provider should do that
if (logger.isDebugEnabled()) {
logger.debug("Successfully Authenticated: "
+ authenticated.toString());
}
SecurityContextHolder.getContext().setAuthentication(authenticated);
} else {
authenticated = SecurityContextHolder.getContext()
.getAuthentication();
if (logger.isDebugEnabled()) {
logger.debug("Previously Authenticated: "
+ authenticated.toString());
}
}
authenticated.setAuthenticated(true);
if (logger.isDebugEnabled()) {
logger.debug("Authenticated: " + authenticated.toString());
}
SecurityContextHolder.getContext().setAuthentication(authenticated);
// Attempt authorization
try {
this.accessDecisionManager.decide(authenticated, object, attr);

View File

@ -1,4 +1,4 @@
/* Copyright 2004 Acegi Technology Pty Limited
/* 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.
@ -40,21 +40,42 @@ public class UsernamePasswordAuthenticationToken
private Object details = null;
private Object principal;
private GrantedAuthority[] authorities;
private boolean authenticated = false;
private boolean authenticated;
//~ Constructors ===========================================================
/**
* This constructor can be safely used by any code that wishes to create a
* <code>UsernamePasswordAuthenticationToken</code>, as the {@link
* #isAuthenticated()} will return <code>false</code>.
*
* @param principal DOCUMENT ME!
* @param credentials DOCUMENT ME!
*/
public UsernamePasswordAuthenticationToken(Object principal,
Object credentials) {
this.principal = principal;
this.credentials = credentials;
this.authenticated = false;
}
/**
* This constructor should only be used by
* <code>AuthenticationManager</code> or
* <code>AuthenticationProvider</code> implementations that are satisfied
* with producing a trusted (ie {@link #isAuthenticated()} =
* <code>true</code>) authentication token.
*
* @param principal
* @param credentials
* @param authorities
*/
public UsernamePasswordAuthenticationToken(Object principal,
Object credentials, GrantedAuthority[] authorities) {
this.principal = principal;
this.credentials = credentials;
this.authorities = authorities;
this.authenticated = true;
}
protected UsernamePasswordAuthenticationToken() {
@ -63,7 +84,13 @@ public class UsernamePasswordAuthenticationToken
//~ Methods ================================================================
public void setAuthenticated(boolean isAuthenticated) {
public void setAuthenticated(boolean isAuthenticated)
throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException(
"Cannot set this token to trusted - use constructor containing GrantedAuthority[]s instead");
}
this.authenticated = isAuthenticated;
}
@ -71,18 +98,6 @@ public class UsernamePasswordAuthenticationToken
return this.authenticated;
}
/**
* Generally you should not call this method, because on subsequent
* requests the <code>Authentication</code> will be recreated by the
* relevant <code>AuthenticationManager</code>. This method is mostly of
* interest to <code>AuthenticationManager</code>s and unit tests.
*
* @param authorities the new authorities to apply
*/
public void setAuthorities(GrantedAuthority[] authorities) {
this.authorities = authorities;
}
public GrantedAuthority[] getAuthorities() {
return this.authorities;
}

View File

@ -18,10 +18,10 @@ package net.sf.acegisecurity.providers.anonymous;
import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.providers.AbstractAuthenticationToken;
import java.io.Serializable;
import org.springframework.util.Assert;
import java.io.Serializable;
/**
* Represents an anonymous <code>Authentication</code>.
@ -35,6 +35,7 @@ public class AnonymousAuthenticationToken extends AbstractAuthenticationToken
private Object principal;
private GrantedAuthority[] authorities;
private boolean authenticated;
private int keyHash;
//~ Constructors ===========================================================
@ -58,14 +59,15 @@ public class AnonymousAuthenticationToken extends AbstractAuthenticationToken
}
for (int i = 0; i < authorities.length; i++) {
Assert.notNull(authorities[i], "Granted authority element "
+ i
+ " is null - GrantedAuthority[] cannot contain any null elements");
Assert.notNull(authorities[i],
"Granted authority element " + i
+ " is null - GrantedAuthority[] cannot contain any null elements");
}
this.keyHash = key.hashCode();
this.principal = principal;
this.authorities = authorities;
this.authenticated = true;
}
protected AnonymousAuthenticationToken() {
@ -74,22 +76,12 @@ public class AnonymousAuthenticationToken extends AbstractAuthenticationToken
//~ Methods ================================================================
/**
* Ignored (always <code>true</code>).
*
* @param isAuthenticated ignored
*/
public void setAuthenticated(boolean isAuthenticated) {
// ignored
this.authenticated = isAuthenticated;
}
/**
* Always returns <code>true</code>.
*
* @return true
*/
public boolean isAuthenticated() {
return true;
return this.authenticated;
}
public GrantedAuthority[] getAuthorities() {

View File

@ -1,4 +1,4 @@
/* Copyright 2004 Acegi Technology Pty Limited
/* 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.
@ -19,12 +19,12 @@ import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.UserDetails;
import net.sf.acegisecurity.providers.AbstractAuthenticationToken;
import org.springframework.util.Assert;
import java.io.Serializable;
import java.util.List;
import org.springframework.util.Assert;
/**
* Represents a successful CAS <code>Authentication</code>.
@ -42,6 +42,7 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken
private String proxyGrantingTicketIou;
private UserDetails userDetails;
private GrantedAuthority[] authorities;
private boolean authenticated;
private int keyHash;
//~ Constructors ===========================================================
@ -79,9 +80,9 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken
}
for (int i = 0; i < authorities.length; i++) {
Assert.notNull(authorities[i], "Granted authority element "
+ i
+ " is null - GrantedAuthority[] cannot contain any null elements");
Assert.notNull(authorities[i],
"Granted authority element " + i
+ " is null - GrantedAuthority[] cannot contain any null elements");
}
this.keyHash = key.hashCode();
@ -91,6 +92,7 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken
this.userDetails = userDetails;
this.proxyList = proxyList;
this.proxyGrantingTicketIou = proxyGrantingTicketIou;
this.authenticated = true;
}
protected CasAuthenticationToken() {
@ -99,22 +101,12 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken
//~ Methods ================================================================
/**
* Ignored (always <code>true</code>).
*
* @param isAuthenticated ignored
*/
public void setAuthenticated(boolean isAuthenticated) {
// ignored
this.authenticated = isAuthenticated;
}
/**
* Always returns <code>true</code>.
*
* @return true
*/
public boolean isAuthenticated() {
return true;
return this.authenticated;
}
public GrantedAuthority[] getAuthorities() {

View File

@ -327,7 +327,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
public Authentication authenticate(Authentication auth)
throws AuthenticationException {
if (auth instanceof UsernamePasswordAuthenticationToken) {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) auth;
UsernamePasswordAuthenticationToken request = (UsernamePasswordAuthenticationToken) auth;
try {
//Create the LoginContext object, and pass our InternallCallbackHandler
@ -340,8 +340,8 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
//create a set to hold the authorities, and add any that have already been applied.
Set authorities = new HashSet();
if (token.getAuthorities() != null) {
authorities.addAll(Arrays.asList(token.getAuthorities()));
if (request.getAuthorities() != null) {
authorities.addAll(Arrays.asList(request.getAuthorities()));
}
//get the subject principals and pass them to each of the AuthorityGranters
@ -368,19 +368,21 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
}
//Convert the authorities set back to an array and apply it to the token.
token.setAuthorities((GrantedAuthority[]) authorities.toArray(
new GrantedAuthority[authorities.size()]));
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(request
.getPrincipal(), request.getCredentials(),
(GrantedAuthority[]) authorities.toArray(
new GrantedAuthority[authorities.size()]));
//Publish the success event
publishSuccessEvent(token);
publishSuccessEvent(result);
//we're done, return the token.
return token;
return result;
} catch (LoginException loginException) {
AcegiSecurityException ase = loginExceptionResolver
.resolveException(loginException);
publishFailureEvent(token, ase);
publishFailureEvent(request, ase);
throw ase;
}
}

View File

@ -42,6 +42,7 @@ public class RememberMeAuthenticationToken extends AbstractAuthenticationToken
private Object principal;
private GrantedAuthority[] authorities;
private int keyHash;
private boolean authenticated;
//~ Constructors ===========================================================
@ -72,6 +73,7 @@ public class RememberMeAuthenticationToken extends AbstractAuthenticationToken
this.keyHash = key.hashCode();
this.principal = principal;
this.authorities = authorities;
this.authenticated = true;
}
protected RememberMeAuthenticationToken() {
@ -80,22 +82,12 @@ public class RememberMeAuthenticationToken extends AbstractAuthenticationToken
//~ Methods ================================================================
/**
* Ignored (always <code>true</code>).
*
* @param isAuthenticated ignored
*/
public void setAuthenticated(boolean isAuthenticated) {
// ignored
this.authenticated = isAuthenticated;
}
/**
* Always returns <code>true</code>.
*
* @return true
*/
public boolean isAuthenticated() {
return true;
return this.authenticated;
}
public GrantedAuthority[] getAuthorities() {

View File

@ -34,6 +34,7 @@ public class RunAsUserToken extends AbstractAuthenticationToken {
private Object principal;
private GrantedAuthority[] authorities;
private int keyHash;
private boolean authenticated;
//~ Constructors ===========================================================
@ -45,6 +46,7 @@ public class RunAsUserToken extends AbstractAuthenticationToken {
this.principal = principal;
this.credentials = credentials;
this.originalAuthentication = originalAuthentication;
this.authenticated = true;
}
protected RunAsUserToken() {
@ -53,22 +55,12 @@ public class RunAsUserToken extends AbstractAuthenticationToken {
//~ Methods ================================================================
/**
* Setting is ignored. Always considered authenticated.
*
* @param ignored DOCUMENT ME!
*/
public void setAuthenticated(boolean ignored) {
// ignored
public void setAuthenticated(boolean isAuthenticated) {
this.authenticated = isAuthenticated;
}
/**
* Always returns <code>true</code>.
*
* @return DOCUMENT ME!
*/
public boolean isAuthenticated() {
return true;
return this.authenticated;
}
public GrantedAuthority[] getAuthorities() {

View File

@ -83,14 +83,14 @@ public class MethodSecurityInterceptorTests extends TestCase {
SecurityContextHolder.getContext().setAuthentication(null);
}
public void testCallingAPublicMethodWhenPresentingAnAuthenticationObjectWillProperlySetItsIsAuthenticatedProperty()
public void testCallingAPublicMethodWhenPresentingAnAuthenticationObjectWillNotChangeItsIsAuthenticatedProperty()
throws Exception {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
"Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_THIS_IS_NOT_REQUIRED_AS_IT_IS_PUBLIC")});
"Password");
assertTrue(!token.isAuthenticated());
SecurityContextHolder.getContext().setAuthentication(token);
// The associated MockAuthenticationManager WILL accept the above UsernamePasswordAuthenticationToken
ITargetObject target = makeInterceptedTarget();
String result = target.publicMakeLowerCase("HELLO");
assertEquals("hello net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken false",
@ -158,13 +158,13 @@ public class MethodSecurityInterceptorTests extends TestCase {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
"Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_LOWER")});
assertTrue(!token.isAuthenticated());
assertTrue(token.isAuthenticated());
SecurityContextHolder.getContext().setAuthentication(token);
ITargetObject target = makeInterceptedTargetWithoutAnAfterInvocationManager();
String result = target.makeLowerCase("HELLO");
// Note we check the isAuthenticated becomes true in following line
// Note we check the isAuthenticated remained true in following line
assertEquals("hello net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken true",
result);
@ -203,11 +203,11 @@ public class MethodSecurityInterceptorTests extends TestCase {
public void testRejectsCallsWhenAuthenticationIsIncorrect()
throws Exception {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
"Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_LOWER")});
"Password");
assertTrue(!token.isAuthenticated());
SecurityContextHolder.getContext().setAuthentication(token);
// NB: The associated MockAuthenticationManager WILL reject the above UsernamePasswordAuthenticationToken
ITargetObject target = makeInterceptedTargetRejectsAuthentication();
try {

View File

@ -1,4 +1,4 @@
/* Copyright 2004 Acegi Technology Pty Limited
/* 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.
@ -51,9 +51,30 @@ public class UsernamePasswordAuthenticationTokenTests extends TestCase {
public void testAuthenticated() {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
"Password", null);
assertTrue(!token.isAuthenticated());
token.setAuthenticated(true);
// check default given we passed some GrantedAuthorty[]s (well, we passed null)
assertTrue(token.isAuthenticated());
// check explicit set to untrusted (we can safely go from trusted to untrusted, but not the reverse)
token.setAuthenticated(false);
assertTrue(!token.isAuthenticated());
// Now let's create a UsernamePasswordAuthenticationToken without any GrantedAuthorty[]s (different constructor)
token = new UsernamePasswordAuthenticationToken("Test", "Password");
assertTrue(!token.isAuthenticated());
// check we're allowed to still set it to untrusted
token.setAuthenticated(false);
assertTrue(!token.isAuthenticated());
// check denied changing it to trusted
try {
token.setAuthenticated(true);
fail("Should have prohibited setAuthenticated(true)");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
public void testGetters() {
@ -67,19 +88,6 @@ public class UsernamePasswordAuthenticationTokenTests extends TestCase {
assertEquals("ROLE_TWO", token.getAuthorities()[1].getAuthority());
}
public void testNewAuthorities() {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
"Password", null);
assertEquals("Test", token.getPrincipal());
assertEquals("Password", token.getCredentials());
assertEquals(null, token.getAuthorities());
token.setAuthorities(new GrantedAuthority[] {new GrantedAuthorityImpl(
"ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO")});
assertEquals("ROLE_ONE", token.getAuthorities()[0].getAuthority());
assertEquals("ROLE_TWO", token.getAuthorities()[1].getAuthority());
}
public void testNoArgConstructor() {
try {
new UsernamePasswordAuthenticationToken();

View File

@ -159,7 +159,6 @@ public class AnonymousAuthenticationTokenTests extends TestCase {
"Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
"ROLE_TWO")});
token2.setAuthenticated(true);
assertFalse(token1.equals(token2));
}
@ -184,7 +183,7 @@ public class AnonymousAuthenticationTokenTests extends TestCase {
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
"ROLE_TWO")});
assertTrue(token.isAuthenticated());
token.setAuthenticated(false); // ignored
assertTrue(token.isAuthenticated());
token.setAuthenticated(false);
assertTrue(!token.isAuthenticated());
}
}

View File

@ -223,7 +223,6 @@ public class CasAuthenticationTokenTests extends TestCase {
"Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
"ROLE_TWO")});
token2.setAuthenticated(true);
assertTrue(!token1.equals(token2));
}
@ -295,15 +294,15 @@ public class CasAuthenticationTokenTests extends TestCase {
assertTrue(!token1.equals(token2));
}
public void testSetAuthenticatedIgnored() {
public void testSetAuthenticated() {
CasAuthenticationToken token = new CasAuthenticationToken("key",
"Test", "Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
"ROLE_TWO")}, makeUserDetails(), new Vector(),
"PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt");
assertTrue(token.isAuthenticated());
token.setAuthenticated(false); // ignored
assertTrue(token.isAuthenticated());
token.setAuthenticated(false);
assertTrue(!token.isAuthenticated());
}
public void testToString() {

View File

@ -159,7 +159,6 @@ public class RememberMeAuthenticationTokenTests extends TestCase {
"Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
"ROLE_TWO")});
token2.setAuthenticated(true);
assertFalse(token1.equals(token2));
}
@ -184,7 +183,7 @@ public class RememberMeAuthenticationTokenTests extends TestCase {
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
"ROLE_TWO")});
assertTrue(token.isAuthenticated());
token.setAuthenticated(false); // ignored
assertTrue(token.isAuthenticated());
token.setAuthenticated(false);
assertTrue(!token.isAuthenticated());
}
}

View File

@ -1,4 +1,4 @@
/* Copyright 2004 Acegi Technology Pty Limited
/* 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.
@ -49,14 +49,14 @@ public class RunAsUserTokenTests extends TestCase {
junit.textui.TestRunner.run(RunAsUserTokenTests.class);
}
public void testAuthenticationSettingAlwaysReturnsTrue() {
public void testAuthenticationSetting() {
RunAsUserToken token = new RunAsUserToken("my_password", "Test",
"Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
"ROLE_TWO")}, UsernamePasswordAuthenticationToken.class);
assertTrue(token.isAuthenticated());
token.setAuthenticated(false);
assertTrue(token.isAuthenticated());
assertTrue(!token.isAuthenticated());
}
public void testGetters() {

View File

@ -26,17 +26,18 @@
</properties>
<body>
<release version="0.9.0" date="In CVS">
<action dev="benalex" type="update">JdbcDaoImpl modified to support synthetic primary keys</action>
<action dev="benalex" type="update">Greatly improve BasicAclEntryAfterInvocationCollectionFilteringProvider performance with large collections (if the principal has access to relatively few collection elements)</action>
<action dev="benalex" type="update">Reorder DaoAuthenticationProvider exception logic as per developer list discussion</action>
<action dev="benalex" type="update">ContextHolder refactored and replaced by SecurityContextHolder</action>
<action dev="benalex" type="fix">Made AclEntry Serializable (correct issue with BasicAclEntryCache)</action>
<action dev="luke_t" type="update">Changed order of credentials verification and expiry checking in DaoAuthenticationProvider. Password must now be successfully verified before expired credentials are reported. </action>
<action dev="benalex" type="update">JdbcDaoImpl modified to support synthetic primary keys</action>
<action dev="benalex" type="update">Greatly improve BasicAclEntryAfterInvocationCollectionFilteringProvider performance with large collections (if the principal has access to relatively few collection elements)</action>
<action dev="benalex" type="update">Reorder DaoAuthenticationProvider exception logic as per developer list discussion</action>
<action dev="benalex" type="update">ContextHolder refactored and replaced by SecurityContextHolder</action>
<action dev="benalex" type="fix">Made AclEntry Serializable (correct issue with BasicAclEntryCache)</action>
<action dev="luke_t" type="update">Changed order of credentials verification and expiry checking in DaoAuthenticationProvider. Password must now be successfully verified before expired credentials are reported. </action>
<action dev="benalex" type="update">AnonymousProcessingFilter offers protected method to control when it should execute</action>
<action dev="benalex" type="fix">AbstractAuthenticationToken.getName() now returns username alone if UserDetails present</action>
<action dev="raykrueger" type="update">AuthorityGranter.grant now returns a java.util.Set of role names, instead of a single role name</action>
<action dev="benalex" type="update">JavaDoc improvements</action>
<action dev="benalex" type="fix">Correct synchronization issue with FilterToBeanProxy initialization</action>
<action dev="raykrueger" type="update">AuthorityGranter.grant now returns a java.util.Set of role names, instead of a single role name</action>
<action dev="benalex" type="update">JavaDoc improvements</action>
<action dev="benalex" type="fix">Correct synchronization issue with FilterToBeanProxy initialization (as per developer list discussion)</action>
<action dev="benalex" type="update">Refactor Authentication.isAuthenticated() handling to be more performance (as per developer list discussion)</action>
</release>
<release version="0.8.2" date="2005-04-20">
<action dev="benalex" type="fix">Correct location of AuthenticationSimpleHttpInvokerRequestExecutor in clientContext.xml</action>