SEC-1495: Convert User class equals and hashcode methods to only use the "username" property.

This prevents situations where other data may have changed when a User object is reloaded (during a subsequent authentication attempt, in which case and Set.contains()/Map.containsKey() will return false even though the collection in question contains a principal representing the same user.
This commit is contained in:
Luke Taylor
2010-06-10 22:27:50 +01:00
parent 27faad3402
commit 8737fe3acb
2 changed files with 38 additions and 64 deletions

View File

@@ -33,8 +33,14 @@ import org.springframework.util.Assert;
* Implemented with value object semantics (immutable after construction, like a <code>String</code>).
* Developers may use this class directly, subclass it, or write their own {@link UserDetails} implementation from
* scratch.
* <p>
* {@code equals} and {@code hashcode} implementations are based on the {@code username} property only, as the
* intention is that lookups of the same user principal object (in a user registry, for example) will match
* where the objects represent the same user, not just when all the properties (authorities, password for
* example) are the same.
*
* @author Ben Alex
* @author Luke Taylor
*/
public class User implements UserDetails {
//~ Instance fields ================================================================================================
@@ -156,61 +162,27 @@ public class User implements UserDetails {
}
}
/**
* Returns {@code true} if the supplied object is a {@code User} instance with the
* same {@code username} value.
* <p>
* In other words, the objects are equal if they have the same username, representing the
* same principal.
*/
@Override
public boolean equals(Object rhs) {
if (!(rhs instanceof User) || (rhs == null)) {
return false;
if (rhs instanceof User) {
return username.equals(((User) rhs).username);
}
User user = (User) rhs;
// We rely on constructor to guarantee any User has non-null
// authorities
if (!authorities.equals(user.authorities)) {
return false;
}
// We rely on constructor to guarantee non-null username and password
return (this.getPassword().equals(user.getPassword()) && this.getUsername().equals(user.getUsername())
&& (this.isAccountNonExpired() == user.isAccountNonExpired())
&& (this.isAccountNonLocked() == user.isAccountNonLocked())
&& (this.isCredentialsNonExpired() == user.isCredentialsNonExpired())
&& (this.isEnabled() == user.isEnabled()));
return false;
}
/**
* Returns the hashcode of the {@code username}.
*/
@Override
public int hashCode() {
int code = 9792;
for (GrantedAuthority authority : getAuthorities()) {
code = code * (authority.hashCode() % 7);
}
if (this.getPassword() != null) {
code = code * (this.getPassword().hashCode() % 7);
}
if (this.getUsername() != null) {
code = code * (this.getUsername().hashCode() % 7);
}
if (this.isAccountNonExpired()) {
code = code * -2;
}
if (this.isAccountNonLocked()) {
code = code * -3;
}
if (this.isCredentialsNonExpired()) {
code = code * -5;
}
if (this.isEnabled()) {
code = code * -7;
}
return code;
return username.hashCode();
}
@Override