3.2 Describing the user
3.2.1 Demystifying the definition of the UserDetails contract
The UserDetails interface
public interface UserDetails extends Serializable {Collection<? extends GrantedAuthority> getAuthorities();String getPassword();String getUsername();boolean isAccountNonExpired();boolean isAccountNonLocked();boolean isCredentialsNonExpired();boolean isEnabled();}
3.2.2 Detailing on the GrantedAuthority contract
public interface GrantedAuthority extends Serializable {String getAuthority();}
two examples of implementing a GrantedAuthority
GrantedAuthority g1 = () -> "READ";GrantedAuthority g2 = new SimpleGrantedAuthority("READ");
3.2.3 Writing a minimal implementation of UserDetails
The DummyUser class
public class DummyUser implements UserDetails {@Overridepublic String getUsername(){return "bill";}@Overridepublic String getPassword(){return "12345";}//...}
Implementation of the getAuthorities() method
public class DummyUser implements UserDetails {//...@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return List.of(() -> "READ");}//...}
Implementation of the last four UserDetails interface methos
public class DummyUser implements UserDetails {@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}}
A more practical implementation of the UserDetails interface
public class User implements UserDetails {private final String username;private final String password;private final String authority;public User(String username, String password, String authority) {this.username = username;this.password = password;this.authority = authority;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return List.of(() -> authority);}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return username;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}}
3.2.4 Using a builder to create instances of the UserDetails type
Constructing a user withe the User builder class
UserDetails u = User.withUsername("bill").password("12345").authorities("read", "write").accountExpired(false).disabled(true).build();
Creating the User.UserBuilder instance ```java User.UserBuilder builder1 = User.withUsername(“bill”);
UserDetails u1 = builder1.password(“12345”) .password(“12345”) .authorities(“read”, “write”) .passwordEncoder(p -> encode(p)) .accountExpired(false) .disabled(true) .build();
User.UserBuilder builder2 = User.withUserDetails(u); UserDetails u2 = builder2.build();
<a name="iAvNr"></a>## 3.2.5 Combing multiple responsibilities related to the userDefining the JPA User entity class```java@Entity@Datapublic class User {@Idprivate Long id;private String username;private String password;private String authority;}
The User class has two responsibilities
@Entitypublic class User implements UserDetails {private final String username;private final String password;private final String authority;public User(String username, String password, String authority) {this.username = username;this.password = password;this.authority = authority;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return List.of(() -> authority);}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return username;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}}
Implementing the User class only as a JPA entitty
@Entitypublic class User {@Idprivate int id;private String username;private String password;private String authority;// Omitted getters and setters}
The SecurityUser class implements the UserDetails contract
public class SecurityUser implements UserDetails {private final User user;public SecurityUser(User user) {this.user = user;}@Overridepublic String getUsername() {return user.getUsername();}@Overridepublic String getPassword() {return user.getPassword();}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return List.of(() -> user.getAuthority());}// Omitted code}
3.3 Instructing Spring Security on how to manage users
3.3.1 Understanding the UserDetailsService contract
public interface UserDetailsService {UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;}
3.3.2 Implementing the UserDetailsService contract
The implementation of the UserDetails interface
public class User implements UserDetails {private final String username;private final String password;private final String authority;public User(String username, String password, String authority) {this.username = username;this.password = password;this.authority = authority;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return List.of(() -> authority);}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return username;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}}
The implementation of the UserDetailsService interface
public class InMemoryUserDetailsService implements UserDetailsService {private final List<UserDetails> users;public InMemoryUserDetailsService(List<UserDetails> users) {this.users = users;}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {return users.stream().filter(u -> u.getUsername().equals(username)).findFirst().orElseThrow(() -> new UsernameNotFoundException("User not found"));}}
UserDetailsService registered as a bean in the configuration class
@Configurationpublic class ProjectConfig {@Beanpublic UserDetailsService userDetailsService() {UserDetails u = new User("john", "12345", "read");List<UserDetails> users = List.of(u);return new InMemoryUserDetailsService(users);}@Beanpublic PasswordEncoder passwordEncoder() {return NoOpPasswordEncoder.getInstance();}}
The definition of the endpoint used for testing the implementation
@RestControllerpublic class HelloController {@GetMapping("/hello")public String hello() {return "Hello";}}
3.3.3 Implementing the UserDetailsManager contract
public interface UserDetailsManager extends UserDetailsService {void createUser(UserDetails user);void updateUser(UserDetails user);void deleteUser(String username);void changePassword(String oldPassword, String newPassword);boolean userExists(String username);}
Registering the JdbcUserDetailsManager in the configuration class
@Configurationpublic class ProjectConfig extends WebSecurityConfigurerAdapter {@Beanpublic UserDetailsService userDetailsService(DataSource dataSource) {return new JdbcUserDetailsManager(dataSource);}@Beanpublic PasswordEncoder passwordEncoder() {return NoOpPasswordEncoder.getInstance();}}
Changing JdbcUserDetailsManager’s queries to find the user
@Configurationpublic class ProjectConfig extends WebSecurityConfigurerAdapter {@Beanpublic UserDetailsService userDetailsService(DataSource dataSource) {String usersByUsernameQuery = "select username, password, enabled from spring.users where username = ?";String authsByUserQuery = "select username, authority from spring.authorities where username = ?";var userDetailsManager = new JdbcUserDetailsManager(dataSource);userDetailsManager.setUsersByUsernameQuery(usersByUsernameQuery);userDetailsManager.setAuthoritiesByUsernameQuery(authsByUserQuery);return userDetailsManager;// return new JdbcUserDetailsManager(dataSource);}@Beanpublic PasswordEncoder passwordEncoder() {return NoOpPasswordEncoder.getInstance();}}


