1
0
mirror of synced 2026-07-20 01:55:08 +00:00

Add UserBuilder Methods

Fixes: gh-5303
This commit is contained in:
Rob Winch
2018-05-03 16:24:54 -05:00
parent 0f612bf637
commit 544e421157
@@ -27,11 +27,16 @@ import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.Assert;
/**
@@ -58,6 +63,8 @@ public class User implements UserDetails, CredentialsContainer {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private static final Log logger = LogFactory.getLog(User.class);
// ~ Instance fields
// ================================================================================================
private String password;
@@ -244,8 +251,99 @@ public class User implements UserDetails, CredentialsContainer {
return sb.toString();
}
/**
* Creates a UserBuilder with a specified user name
*
* @param username the username to use
* @return the UserBuilder
*/
public static UserBuilder withUsername(String username) {
return new UserBuilder().username(username);
return builder().username(username);
}
/**
* Creates a UserBuilder
*
* @return the UserBuilder
*/
public static UserBuilder builder() {
return new UserBuilder();
}
/**
* <p>
* <b>WARNING:</b> This method is considered unsafe for production and is only intended
* for sample applications.
* </p>
* <p>
* Creates a user and automatically encodes the provided password using
* {@code PasswordEncoderFactories.createDelegatingPasswordEncoder()}. For example:
* </p>
*
* <pre>
* <code>
* UserDetails user = User.withDefaultPasswordEncoder()
* .username("user")
* .password("password")
* .roles("USER")
* .build();
* // outputs {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
* System.out.println(user.getPassword());
* </code>
* </pre>
*
* This is not safe for production (it is intended for getting started experience)
* because the password "password" is compiled into the source code and then is
* included in memory at the time of creation. This means there are still ways to
* recover the plain text password making it unsafe. It does provide a slight
* improvement to using plain text passwords since the UserDetails password is
* securely hashed. This means if the UserDetails password is accidentally exposed,
* the password is securely stored.
*
* In a production setting, it is recommended to hash the password ahead of time.
* For example:
*
* <pre>
* <code>
* PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
* // outputs {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
* // remember the password that is printed out and use in the next step
* System.out.println(encoder.encode("password"));
* </code>
* </pre>
*
* <pre>
* <code>
* UserDetails user = User.withUsername("user")
* .password("{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG")
* .roles("USER")
* .build();
* </code>
* </pre>
*
* @return a UserBuilder that automatically encodes the password with the default
* PasswordEncoder
* @deprecated Using this method is not considered safe for production, but is
* acceptable for demos and getting started. For production purposes, ensure the
* password is encoded externally. See the method Javadoc for additional details.
* There are no plans to remove this support. It is deprecated to indicate
* that this is considered insecure for production purposes.
*/
@Deprecated
public static UserBuilder withDefaultPasswordEncoder() {
logger.warn("User.withDefaultPasswordEncoder() is considered unsafe for production and is only intended for sample applications.");
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
return builder().passwordEncoder(encoder);
}
public static UserBuilder withUserDetails(UserDetails userDetails) {
return withUsername(userDetails.getUsername())
.password(userDetails.getPassword())
.accountExpired(!userDetails.isAccountNonExpired())
.accountLocked(!userDetails.isAccountNonLocked())
.authorities(userDetails.getAuthorities())
.credentialsExpired(!userDetails.isCredentialsNonExpired())
.disabled(!userDetails.isEnabled());
}
/**
@@ -260,6 +358,7 @@ public class User implements UserDetails, CredentialsContainer {
private boolean accountLocked;
private boolean credentialsExpired;
private boolean disabled;
private PasswordEncoder passwordEncoder = NoOpPasswordEncoder.getInstance();
/**
* Creates a new instance
@@ -274,7 +373,7 @@ public class User implements UserDetails, CredentialsContainer {
* @return the {@link UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
private UserBuilder username(String username) {
public UserBuilder username(String username) {
Assert.notNull(username, "username cannot be null");
this.username = username;
return this;
@@ -293,6 +392,20 @@ public class User implements UserDetails, CredentialsContainer {
return this;
}
/**
* Encodes the current password (if non-null) and any future passwords supplied
* to {@link #password(String)}.
*
* @param encoder the encoder to use
* @return the {@link UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
private UserBuilder passwordEncoder(PasswordEncoder encoder) {
Assert.notNull(encoder, "encoder cannot be null");
this.passwordEncoder = encoder;
return this;
}
/**
* Populates the roles. This method is a shortcut for calling
* {@link #authorities(String...)}, but automatically prefixes each entry with
@@ -351,7 +464,7 @@ public class User implements UserDetails, CredentialsContainer {
* additional attributes for this user)
* @see #roles(String...)
*/
public UserBuilder authorities(List<? extends GrantedAuthority> authorities) {
public UserBuilder authorities(Collection<? extends GrantedAuthority> authorities) {
this.authorities = new ArrayList<GrantedAuthority>(authorities);
return this;
}
@@ -418,7 +531,8 @@ public class User implements UserDetails, CredentialsContainer {
}
public UserDetails build() {
return new User(username, password, !disabled, !accountExpired,
String encodedPassword = this.passwordEncoder.encode(password);
return new User(username, encodedPassword, !disabled, !accountExpired,
!credentialsExpired, !accountLocked, authorities);
}
}