SpringSecurity根據(jù)自定義異常返回登錄錯誤提示信息(賬戶鎖定)
一、背景
當前場景:用戶在登錄失敗需要根據(jù)不同的場景返回不同的提示信息,例如賬號不存在或密碼輸錯提示 “用戶名或密碼錯誤”,賬號禁用是提示 "賬戶被鎖定"等,默認輸錯5次密碼后賬戶會被鎖定。
需求:當最后一次輸錯密碼時需要給用戶提示出 “xxx賬戶將被鎖定” 的提示,而不是提示 “用戶名或密碼錯誤”
分析:前四次輸錯提示 “用戶名或密碼錯誤”,第5次輸錯提示 “xxx賬戶將被鎖定”,那么需要在密碼校驗失敗時,獲取到這是第幾次輸錯
二、實現(xiàn)方案
2.1 登錄失敗記錄輸錯次數(shù)
@Component
public class LoginFailedListener implements ApplicationListener<AbstractAuthenticationFailureEvent> {
@Autowired
private CustomProperties customProperties ;
@Autowired
private UserDao userDao;
@Override
public void onApplicationEvent(AbstractAuthenticationFailureEvent abstractAuthenticationFailureEvent) {
String loginName = abstractAuthenticationFailureEvent.getAuthentication()
.getName();
if (log.isInfoEnabled()) {
log.info("登錄失敗 loginName=[{}]", loginName);
}
if (StringUtils.isBlank(loginName)) {
return;
}
//查詢登錄賬號是否存在
UserDo condition = new UserDo();
condition.setLoginName(loginName);
List<UserDo> userDos = userDao.selectByRecord(condition);
if (CollectionUtils.isEmpty(userDos)) {
return;
}
UserDo userDo = userDos.get(0);
UserDo updateDo = new UserDo();
updateDo.setUserId(userDo.getUserId());
Integer loginFailMaxCount = customroperties.getLoginFailMaxCount();
if (loginFailMaxCount <= userDo.getTryTime() + 1) {
//更新用戶狀態(tài)為凍結(jié)
updateDo.setStatus(EnumUserStatus.FORBIDDEN.getCode());
updateDo.setTryTime(loginFailMaxCount);
if (log.isInfoEnabled()) {
log.info("登錄次數(shù)已達最大次數(shù):{} 凍結(jié)賬戶 loginName=[{}]", loginFailMaxCount, loginName);
}
} else {
//更新嘗試次數(shù)
updateDo.setTryTime(userDo.getTryTime() + 1);
if (log.isInfoEnabled()) {
log.info("嘗試次數(shù)+1 loginName=[{}], tryTime=[{}]", loginName, updateDo.getTryTime());
}
}
updateDo.setUpdateTime(new Date());
userDao.updateBySelective(updateDo);
}
}
2.2 重寫校驗方法,滿足條件時拋出自定義異常
- 校驗拋出的異常一定要是AuthenticationException及其子類
- 因為創(chuàng)建了監(jiān)聽器ApplicationListener的實現(xiàn),源碼中回調(diào)是有條件的,所以最后最好是原樣拋出 BadCredentialsException ,否則ApplicationListener將不會被觸發(fā)
@Slf4j
public class CustomDaoAuthenticationProvider extends DaoAuthenticationProvider {
@Autowired
private CustomProperties customProperties ;
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
try {
super.additionalAuthenticationChecks(userDetails, authentication);
} catch (AuthenticationException e) {
if(e instanceof BadCredentialsException && userDetails instanceof UserDto){
UserDto userDto = (UserDto) userDetails;
//先到這里,然后去觸發(fā)LoginFailedListener,達到賬戶被鎖定這里需要+1
//已經(jīng)被凍結(jié)的不處理,只處理正常用戶,并且是達到最大失敗次數(shù)的那一次
if(EnumUserStatus.NORMAL.getCode().equals(userDto.getStatus()) && Objects.equals(userDto.getTryTime() + 1, customProperties .getLoginFailMaxCount())){
if(log.isErrorEnabled()){
log.error("用戶:{} 登錄失敗次數(shù)已達最大,賬戶將被鎖定", userDto.getLoginName());
}
throw new BadCredentialsException(e.getMessage(), new LoginFailCountOutException("登錄失敗次數(shù)已達最大"));
}else {
throw e;
}
}else {
throw e;
}
}
}
}
public class LoginFailCountOutException extends AuthenticationException {
private static final long serialVersionUID = -8546980609242201580L;
/**
* Constructs an {@code AuthenticationException} with the specified message and no
* root cause.
*
* @param msg the detail message
*/
public LoginFailCountOutException(String msg) {
super(msg);
}
public LoginFailCountOutException(String msg, Throwable ex) {
super(msg, ex);
}
}
2.3 裝配自定義的AuthenticationProvider
@Bean
public AuthenticationProvider authenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder, UserAuthoritiesMapper userAuthoritiesMapper) {
DaoAuthenticationProvider authenticationProvider = new CustomDaoAuthenticationProvider();
// 對默認的UserDetailsService進行覆蓋
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder);
authenticationProvider.setAuthoritiesMapper(userAuthoritiesMapper);
return authenticationProvider;
}
WebSecurityConfigurerAdapter 的配置類中注入
@Autowired
private AuthenticationProvider authenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
2.4 AuthenticationFailureHandler 根據(jù)異常返回提示
failureHandler((request, response, ex) -> {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
String errorMessage = this.loginFailureErrorMessage(ex);
out.write(JSON.toJSONString(Response.<Void>builder().isSuccess(false)
.errorMessage(errorMessage)
.build()));
out.flush();
out.close();
})
private String loginFailureErrorMessage(AuthenticationException ex) {
if (ex instanceof UsernameNotFoundException || ex instanceof BadCredentialsException) {
if(ex.getCause() != null && ex.getCause() instanceof LoginFailCountOutException){
return "登錄失敗次數(shù)已達最大限制, 賬戶凍結(jié)";
}
return "用戶名或密碼錯誤";
}
if (ex instanceof DisabledException) {
return "賬戶被禁用";
}
if (ex instanceof LockedException) {
return "賬戶被鎖定";
}
if (ex instanceof AccountExpiredException) {
return "賬戶已過期";
}
if (ex instanceof CredentialsExpiredException) {
return "密碼已過期";
}
log.warn("不明原因登錄失敗", ex);
return "登錄失敗";
}
到此這篇關于SpringSecurity根據(jù)自定義異常返回登錄錯誤提示信息(賬戶鎖定)的文章就介紹到這了,更多相關SpringSecurity自定義異常返回登錄錯誤提示內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
java?byte數(shù)組轉(zhuǎn)String的幾種常用方法
在Java中數(shù)組是一種非常常見的數(shù)據(jù)結(jié)構(gòu),它可以用來存儲多個相同類型的數(shù)據(jù),有時候,我們需要將數(shù)組轉(zhuǎn)換為字符串,以便于輸出或者傳遞給其他方法,這篇文章主要給大家介紹了關于java?byte數(shù)組轉(zhuǎn)String的幾種常用方法,需要的朋友可以參考下2024-09-09
java訪問者模式的靜態(tài)動態(tài)及偽動態(tài)分派徹底理解
這篇文章主要為大家介紹了java訪問者模式的靜態(tài)動態(tài)及偽動態(tài)分派徹底理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06
gateway與spring-boot-starter-web沖突問題的解決
這篇文章主要介紹了gateway與spring-boot-starter-web沖突問題的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
Java利用DelayQueue實現(xiàn)延遲任務代碼實例
這篇文章主要介紹了Java利用DelayQueue實現(xiàn)延遲任務代碼實例,DelayQueue?是一個支持延時獲取元素的阻塞隊列,?內(nèi)部采用優(yōu)先隊列?PriorityQueue?存儲元素,同時元素必須實現(xiàn)?Delayed?接口,需要的朋友可以參考下2023-12-12
SpringCloud讀取Nacos配置中心報錯及遇到的坑:Could?not?resolve?placehold
這篇文章主要介紹了SpringCloud讀取Nacos配置中心報錯:Could?not?resolve?placeholder?‘xxx’?in?value?‘${xxx},本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03

