O logowaniu użytkowników pisałam wcześniej w następujących postach:
- Spring Boot: Bezpieczeństwo 101
- Własny formularz logowania Spring Security
- Zapisuję dane użytkowników w bazie i robię głupie błędy podczas walidacji formularza
Dzisiaj dodałam do mojej raczkującej aplikacji funkcjonalność logowania użytkowników zapisanych wcześniej w bazie danych.
Poniżej przedstawiam zmiany, które musiałam w tym celu wprowadzić do mojej aplikacji.
a) Wersja podstawowa
W klasie User
, która od teraz ma być nie tylko klasą przechowującą dane, ale także bazą do autoryzacji użytkowników, muszę zaimplementować interfejs UserDetails
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
@Document public class User implements UserDetails { ... @Override public Collection<? extends GrantedAuthority> getAuthorities() { return Arrays.asList(new SimpleGrantedAuthority("USER")); } @Override public String getPassword() { return passwordEncrypted; } public void setPassword(String password) { this.passwordEncrypted = password; } @Override public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } ... @Override public boolean isEnabled() { return isConfirmationStatus(); } ... } |
W klasie SecurityConfiguration
opisuję nowy sposób logowania:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@Configuration @EnableGlobalMethodSecurity(securedEnabled = true) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { ... @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(new UserDetailsService() { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userRepository.getUserByUsername(username); } }); } ... } |
Brakuje mi odpowiedniej metody do pobrania obiektu użytkownika po jego nazwie (getUserByUsername
) w klasie UserRepository
. Do tej pory szukałam użytkowników jedynie na podstawie confirmationId
(co opisałam tutaj: Aktywacja konta poprzez email). Na szczęście dzięki Spring Data nie muszę jej nawet implementować, wystarczy, że dodam następującą linię:
1 2 3 4 5 6 |
public interface UserRepository extends CrudRepository<User, BigInteger>{ public User getUserByConfirmationId(String confirmationId); public User getUserByUsername(String username); } |
b) Wersja rozszerzona. Przecież ja szyfruję hasła!
W tym celu muszę zarejestrować odpowiedni bean szyfrujący:
1 2 3 4 |
@Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } |
Jego właśnie muszę użyć do zaszyfrowania hasła przy tworzeniu nowego użytkownika:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@Controller public class UserController extends WebMvcConfigurerAdapter { ... @Autowired PasswordEncoder passwordEncoder; ... ... user.setPasswordEncrypted(passwordEncoder.encode(user.getPassword1())); ... ... } |
Należy też wspomnieć o nim w konfiguracji:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@Configuration @EnableGlobalMethodSecurity(securedEnabled = true) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { ... @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(new UserDetailsService() { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userRepository.getUserByUsername(username); } }).passwordEncoder(passwordEncoder()); } ... } |
Klasa BCryptPasswordEncoder
implementuję metodę matches
z interfejsu PasswordEncoder
, która sprawdza, czy hasło w czystej postaci pasuje do jego zahaszowanej wersji przechowywanej w bazie danych.
Już.
PS. Podczas testowania mój dostawca domeny i serwera zablokował mi konto email, przekonany, że ktoś się na nie włamał i rozsyła spam ❤
W linijce
new SimpleGrantedAuthority(“USER”)
nie powinno być “ROLE_USER”? Inaczej coś nie działało 🙂