spring security User 源码
spring security User 代码
文件路径:/core/src/main/java/org/springframework/security/core/userdetails/User.java
/*
* Copyright 2004, 2005, 2006 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
*
* https://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.springframework.security.core.userdetails;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Function;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
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.PasswordEncoder;
import org.springframework.util.Assert;
/**
* Models core user information retrieved by a {@link UserDetailsService}.
* <p>
* 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.
* <p>
* Note that this implementation is not immutable. It implements the
* {@code CredentialsContainer} interface, in order to allow the password to be erased
* after authentication. This may cause side-effects if you are storing instances
* in-memory and reusing them. If so, make sure you return a copy from your
* {@code UserDetailsService} each time it is invoked.
*
* @author Ben Alex
* @author Luke Taylor
*/
public class User implements UserDetails, CredentialsContainer {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private static final Log logger = LogFactory.getLog(User.class);
private String password;
private final String username;
private final Set<GrantedAuthority> authorities;
private final boolean accountNonExpired;
private final boolean accountNonLocked;
private final boolean credentialsNonExpired;
private final boolean enabled;
/**
* Calls the more complex constructor with all boolean arguments set to {@code true}.
*/
public User(String username, String password, Collection<? extends GrantedAuthority> authorities) {
this(username, password, true, true, true, true, authorities);
}
/**
* Construct the <code>User</code> with the details required by
* {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider}.
* @param username the username presented to the
* <code>DaoAuthenticationProvider</code>
* @param password the password that should be presented to the
* <code>DaoAuthenticationProvider</code>
* @param enabled set to <code>true</code> if the user is enabled
* @param accountNonExpired set to <code>true</code> if the account has not expired
* @param credentialsNonExpired set to <code>true</code> if the credentials have not
* expired
* @param accountNonLocked set to <code>true</code> if the account is not locked
* @param authorities the authorities that should be granted to the caller if they
* presented the correct username and password and the user is enabled. Not null.
* @throws IllegalArgumentException if a <code>null</code> value was passed either as
* a parameter or as an element in the <code>GrantedAuthority</code> collection
*/
public User(String username, String password, boolean enabled, boolean accountNonExpired,
boolean credentialsNonExpired, boolean accountNonLocked,
Collection<? extends GrantedAuthority> authorities) {
Assert.isTrue(username != null && !"".equals(username) && password != null,
"Cannot pass null or empty values to constructor");
this.username = username;
this.password = password;
this.enabled = enabled;
this.accountNonExpired = accountNonExpired;
this.credentialsNonExpired = credentialsNonExpired;
this.accountNonLocked = accountNonLocked;
this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
}
@Override
public Collection<GrantedAuthority> getAuthorities() {
return this.authorities;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
@Override
public boolean isAccountNonExpired() {
return this.accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return this.accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return this.credentialsNonExpired;
}
@Override
public void eraseCredentials() {
this.password = null;
}
private static SortedSet<GrantedAuthority> sortAuthorities(Collection<? extends GrantedAuthority> authorities) {
Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection");
// Ensure array iteration order is predictable (as per
// UserDetails.getAuthorities() contract and SEC-717)
SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<>(new AuthorityComparator());
for (GrantedAuthority grantedAuthority : authorities) {
Assert.notNull(grantedAuthority, "GrantedAuthority list cannot contain any null elements");
sortedAuthorities.add(grantedAuthority);
}
return sortedAuthorities;
}
/**
* 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 obj) {
if (obj instanceof User) {
return this.username.equals(((User) obj).username);
}
return false;
}
/**
* Returns the hashcode of the {@code username}.
*/
@Override
public int hashCode() {
return this.username.hashCode();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getName()).append(" [");
sb.append("Username=").append(this.username).append(", ");
sb.append("Password=[PROTECTED], ");
sb.append("Enabled=").append(this.enabled).append(", ");
sb.append("AccountNonExpired=").append(this.accountNonExpired).append(", ");
sb.append("credentialsNonExpired=").append(this.credentialsNonExpired).append(", ");
sb.append("AccountNonLocked=").append(this.accountNonLocked).append(", ");
sb.append("Granted Authorities=").append(this.authorities).append("]");
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 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::encode);
}
public static UserBuilder withUserDetails(UserDetails userDetails) {
// @formatter:off
return withUsername(userDetails.getUsername())
.password(userDetails.getPassword())
.accountExpired(!userDetails.isAccountNonExpired())
.accountLocked(!userDetails.isAccountNonLocked())
.authorities(userDetails.getAuthorities())
.credentialsExpired(!userDetails.isCredentialsNonExpired())
.disabled(!userDetails.isEnabled());
// @formatter:on
}
private static class AuthorityComparator implements Comparator<GrantedAuthority>, Serializable {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
@Override
public int compare(GrantedAuthority g1, GrantedAuthority g2) {
// Neither should ever be null as each entry is checked before adding it to
// the set. If the authority is null, it is a custom authority and should
// precede others.
if (g2.getAuthority() == null) {
return -1;
}
if (g1.getAuthority() == null) {
return 1;
}
return g1.getAuthority().compareTo(g2.getAuthority());
}
}
/**
* Builds the user to be added. At minimum the username, password, and authorities
* should provided. The remaining attributes have reasonable defaults.
*/
public static final class UserBuilder {
private String username;
private String password;
private List<GrantedAuthority> authorities;
private boolean accountExpired;
private boolean accountLocked;
private boolean credentialsExpired;
private boolean disabled;
private Function<String, String> passwordEncoder = (password) -> password;
/**
* Creates a new instance
*/
private UserBuilder() {
}
/**
* Populates the username. This attribute is required.
* @param username the username. Cannot be null.
* @return the {@link UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public UserBuilder username(String username) {
Assert.notNull(username, "username cannot be null");
this.username = username;
return this;
}
/**
* Populates the password. This attribute is required.
* @param password the password. Cannot be null.
* @return the {@link UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public UserBuilder password(String password) {
Assert.notNull(password, "password cannot be null");
this.password = password;
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)
*/
public UserBuilder passwordEncoder(Function<String, String> 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
* "ROLE_". This means the following:
*
* <code>
* builder.roles("USER","ADMIN");
* </code>
*
* is equivalent to
*
* <code>
* builder.authorities("ROLE_USER","ROLE_ADMIN");
* </code>
*
* <p>
* This attribute is required, but can also be populated with
* {@link #authorities(String...)}.
* </p>
* @param roles the roles for this user (i.e. USER, ADMIN, etc). Cannot be null,
* contain null values or start with "ROLE_"
* @return the {@link UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public UserBuilder roles(String... roles) {
List<GrantedAuthority> authorities = new ArrayList<>(roles.length);
for (String role : roles) {
Assert.isTrue(!role.startsWith("ROLE_"),
() -> role + " cannot start with ROLE_ (it is automatically added)");
authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
}
return authorities(authorities);
}
/**
* Populates the authorities. This attribute is required.
* @param authorities the authorities for this user. Cannot be null, or contain
* null values
* @return the {@link UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
* @see #roles(String...)
*/
public UserBuilder authorities(GrantedAuthority... authorities) {
return authorities(Arrays.asList(authorities));
}
/**
* Populates the authorities. This attribute is required.
* @param authorities the authorities for this user. Cannot be null, or contain
* null values
* @return the {@link UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
* @see #roles(String...)
*/
public UserBuilder authorities(Collection<? extends GrantedAuthority> authorities) {
this.authorities = new ArrayList<>(authorities);
return this;
}
/**
* Populates the authorities. This attribute is required.
* @param authorities the authorities for this user (i.e. ROLE_USER, ROLE_ADMIN,
* etc). Cannot be null, or contain null values
* @return the {@link UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
* @see #roles(String...)
*/
public UserBuilder authorities(String... authorities) {
return authorities(AuthorityUtils.createAuthorityList(authorities));
}
/**
* Defines if the account is expired or not. Default is false.
* @param accountExpired true if the account is expired, false otherwise
* @return the {@link UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public UserBuilder accountExpired(boolean accountExpired) {
this.accountExpired = accountExpired;
return this;
}
/**
* Defines if the account is locked or not. Default is false.
* @param accountLocked true if the account is locked, false otherwise
* @return the {@link UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public UserBuilder accountLocked(boolean accountLocked) {
this.accountLocked = accountLocked;
return this;
}
/**
* Defines if the credentials are expired or not. Default is false.
* @param credentialsExpired true if the credentials are expired, false otherwise
* @return the {@link UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public UserBuilder credentialsExpired(boolean credentialsExpired) {
this.credentialsExpired = credentialsExpired;
return this;
}
/**
* Defines if the account is disabled or not. Default is false.
* @param disabled true if the account is disabled, false otherwise
* @return the {@link UserBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public UserBuilder disabled(boolean disabled) {
this.disabled = disabled;
return this;
}
public UserDetails build() {
String encodedPassword = this.passwordEncoder.apply(this.password);
return new User(this.username, encodedPassword, !this.disabled, !this.accountExpired,
!this.credentialsExpired, !this.accountLocked, this.authorities);
}
}
}
相关信息
相关文章
spring security AuthenticationUserDetailsService 源码
spring security MapReactiveUserDetailsService 源码
spring security ReactiveUserDetailsPasswordService 源码
spring security ReactiveUserDetailsService 源码
spring security UserDetails 源码
spring security UserDetailsByNameServiceWrapper 源码
spring security UserDetailsChecker 源码
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦