我們在AppConfig.java的formLogin後面再增加以下內容,將登出的網址設定為/logout,以及登出後會轉址到/login?logout
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout"))
修改users.html加入登出按鈕
...
<a th:href="@{/}">Home</a>
<a th:href="@{/logout}">Logout</a>
...
我們在login.html添加登出的訊息,順便增加在登入錯誤時,顯示email或密碼有錯的訊息。
如果網址是login?logout,代表已經登出,顯示You've logged out.
如果是login?error,代表email或密碼有錯,則是Wrong email or password
...
<h3 th:if="${param.logout}">You've logged out.</h3>
<h3 th:if="${param.error}">Wrong email or password</h3>
<form method="post" role="form" th:action="@{/login}">
...
我們啟動專案,嘗試登出,觀察登出後顯示的訊息。
輸入錯誤的密碼,進行登入,可以看到錯誤訊息。
我們先看一下資料庫中儲存的內容,有id、email和密碼,有沒有覺得怪怪的,或許你現在沒那個感覺,但是當我說出答案時就會恍然大悟。
使用大型網站(如Google)時忘記密碼,通常寄來是一封帶有密碼重置連結的email,而小型網站很貼心把原本的密碼寄給我,不用重置密碼讓人感覺很好。
大網站為什麼不這麽做呢?因為大網站是將密碼用雜湊(Hash)函式得到的雜湊數值存進資料庫,他不知道原本的密碼是什麼,只有雜湊數值,使用雜湊數值回推原始的密碼是很困難的一件事,直接重置密碼比較快。
我們的專案現在就像是小型網站的做法,稱呼為明文儲存密碼,把原始密碼存在資料庫中,用戶忘記就能傳給他。
明文儲存有個很大的問題,駭客破解資料庫後就能拿到密碼,接著密碼就會被到處散播,如果Gmail、Facebook也使用相同的密碼,就會發現帳號每天被駭客嘗試登入許多次。
明文儲存對用戶來說太危險了,我們要遠離這種網站,檢驗的方式就是「忘記密碼」,重置的是好網站,回傳明文密碼的是壞網站。
我們在Spring Security中開啟雜湊函式,在AppConfig.java加入
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
在UserService.java將密碼雜湊後存入資料庫
public void createUser(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
userRepository.save(user);
}
這樣用戶的密碼就能被安全的保存了。
建立CustomUserDetailsServiceImpl.java,驗證用戶的email和密碼。
//CustomUserDetailsServiceImpl.java
@Service
public class CustomUserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
根據email取得用戶的資訊,並驗證email和密碼是否正確
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findUserByEmail(email);
如果找不到用戶,就回傳找不到使用這個email的用戶
if (user == null) {
throw new UsernameNotFoundException("User not found with email :" + email);
}
給用戶ROLE_USER權限
List<GrantedAuthority> authorities = List.of(new SimpleGrantedAuthority("ROLE_USER"));
驗證用戶的email和密碼是否正確,正確就可以登入,如果email或密碼有誤拒絕登入。
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), authorities);
}
}
我們刪除資料庫中的舊資料(密碼沒雜湊的部分),啟動MySQL Client (MariaDB (x64)),先查詢哪些用戶的密碼沒經過雜湊
USE user_db;
SELECT * FROM users WHERE password NOT LIKE "$%";
雜湊後的密碼是$開頭的內容,我們尋找密碼不是$開頭的,那些就是舊的資料,顯示在螢幕上。
確認螢幕上的資料沒錯後,刪除舊資料。
在刪除之前先用SELECT查看一下,再DELETE是個好習慣,可以避免成為「MySQL從刪庫到跑路」的主角。
刪除舊用戶
DELETE FROM users WHERE password NOT LIKE "$%";
啟動專案,我們先註冊新用戶再登入,確認新的登入驗證系統正常運作。
我們的專案就完成了。
需要自行編寫application.properties
https://mega.nz/file/0NNCkbTL#x0kGEOZESCHaU_dkrVh41dgzT_LYNJ_RPWwmUGPCtA8