二、概要說明
- 上文已詳細介紹了四、Spring Boot整合Spring Security之認證流程
- 本文則著重介紹使用者名稱密碼認證過濾器UsernamePasswordAuthenticationFilter的實現原理過程
- 認證管理器(authenticationManager)
- 認證提供者(AuthenticationProvider)
- 自定義配置使用者名稱密碼實現(UserDetailsService)
三、UsernamePasswordAuthenticationFilter
1、結構及作用
- 繼承AbstractAuthenticationProcessingFilter
- 初始化請求地址
- 初始化authenticationManager
- 初始化successHandler
- 初始化failureHandler
- 實現過濾器入口doFilter方法
- doFilter方法呼叫抽象方法attemptAuthentication,attemptAuthentication供子類實現完成使用者名稱密碼驗證業務
- 認證成功時更新安全上下文,並呼叫successHandler.onAuthenticationSuccess
- 認證失敗時刪除安全上下文,並呼叫failureHandler.onAuthenticationFailure
- 實現attemptAuthentication方法
- 從請求中獲取使用者名稱密碼
- 生成未認證的Authentication
- 呼叫authenticationManager的authenticate方法完成使用者名稱密碼驗證
四、認證管理器(AuthenticationManager)
1、作用
- 完成Authentication的認證
2、ProviderManager(預設實現)
- ProviderManager實現AuthenticationManager介面
- AuthenticationManager的作用的是完成Authentication的認證
- 但是ProviderManager並未直接完成Authentication的認證
- 而是提供一個AuthenticationProvider集合
- 遍歷AuthenticationProvider集合來完成Authentication的認證
- 當需要多種認證方式時,可以註冊自定義的AuthenticationProvider,後續介紹註冊方式
五、AuthenticationProvider
1、作用
- 呼叫介面獲取使用者資訊UserDetails
- 驗證使用者及密碼是否可用
2、DaoAuthenticationProvider(預設實現)
- DaoAuthenticationProvider繼承AbstractUserDetailsAuthenticationProvider實現AuthenticationProvider介面
- 呼叫retrieveUser方法獲取使用者資訊UserDetails
- 呼叫userDetailsService.loadUserByUsername獲取使用者資訊UserDetails
- 驗證使用者是否存在並可用,不存在或者不可用時拋異常(過期、鎖定、啟用)
- 驗證密碼是否可用,不可用時拋異常(為空、過期)
- 使用密碼加密器校驗密碼(介面輸入的密碼和資料庫已加密的密碼)
- 密碼不一致時拋異常
六、UserDetailsService
1、作用
- 透過使用者名稱username獲取使用者資訊UserDetails
- 返回使用者資訊UserDetails
2、InMemoryUserDetailsManager(預設實現)
- 專案啟動時會預設生成一個使用者名稱密碼,存在記憶體中
- 透過使用者名稱獲取該使用者並返回
3、推薦實現:自定義UserDetailsService
- 透過使用者名稱從資料庫中獲取到使用者
- 資料庫使用者轉為UserDetails,資料庫中未設定的屬性像是否啟用、賬號未過期、密碼未過期、賬號未鎖定直接設定為true即可
package com.yu.demo.service.impl;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
//@Autowired
//private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//TODO 透過username從資料庫中獲取使用者,將使用者轉UserDetails
//User user = userService.getByUsername(username);
//return new User(username, user.getPassword(), user.getEnable(), user.getAccountNonExpired(), user.getCredentialsNonExpired(), user.getAccountNonLocked(), user.getAuthorities());
//{noop}不使用密碼加密器,密碼123的都可以驗證成功
return new User(username, "{noop}123", true, true, true, true, AuthorityUtils.NO_AUTHORITIES);
}
}