背景
由於原來登錄表單和oauth/token使用時,是走明文傳輸的。爲了保證傳輸中不容易被竊取,採用RSA加密明文密碼後再進行傳輸,後臺仍然是BCrypt的方式存入數據庫。但通過查閱資料,有些寫法是通過繼承PasswordEncoder的方式實現,但在5.x版本中,自定義的加密器會缺少id,所以需要換個方式實現。
版本
-
Spring Security:5.x
預定方案
- 或許可以註冊增加一個類似UsernamePasswordAuthenticationFilter的filter,加入到filter鏈(沒嘗試過,本文不開展)
- 增加AuthenticationProvider身份認證模式實現,本文由此開展
步驟
定義一個RSA密文身份認證類
核心就是從認證信息authentication中拿到密文,調用RSA工具解密,再將解密後的密碼與數據庫密碼進行BCrypt對比,正確則構造token信息,錯誤則拋出異常。
@Component
public class CustomUserAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 認證處理,返回一個Authentication的實現類則代表認證成功,拋出CredentialsExpiredException則失敗
* @param authentication 認證信息
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
//獲取用戶信息
UserDetails user = userDetailsService.loadUserByUsername(username);
//比較前端傳入的密碼和數據庫中加密的密碼是否相等
String plainText = RSAUtil.decrypt(password);
if (!passwordEncoder.matches(plainText, user.getPassword())) {
throw new CredentialsExpiredException("密碼不一致");
}
//獲取用戶權限信息
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
return new UsernamePasswordAuthenticationToken(user, password, authorities);
}
/**
* 如果該AuthenticationProvider支持傳入的Authentication對象,則返回true
*/
@Override
public boolean supports(Class<?> clazz) {
return clazz.equals(UsernamePasswordAuthenticationToken.class);
}
}
將認證類配置到Security配置裏
在Security配置類中,構造實例並裝載到Bean中,並設置爲AuthenticationManagerBuilder的一個參數。
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebServerSecurityConfig extends WebSecurityConfigurerAdapter {
// 不相關代碼略去
/**
* 注入自定義的userDetailsService實現,獲取用戶信息,設置密碼加密方式
*
* @param authenticationManagerBuilder
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder())
.and()
.authenticationProvider(userAuthenticationProvider())
.eraseCredentials(false); // 不清除密碼,後續需要手動清除
}
/**
* BCrypt加密器
* @return 加密器
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 自定義認證操作
* @return 認證操作
*/
@Bean
public AuthenticationProvider userAuthenticationProvider() {
return new CustomUserAuthenticationProvider();
}
}
參考資料
https://blog.csdn.net/yaomingyang/article/details/98785488