● 이슈 발생 시점
Spring Security + JWT를 이용해 인증, 인가 기능을 구현하던 중 UserDetailsService에서 loadUserByUsername함수에서 id로 DB 조회 시 데이터베이스에서 찾을 수 없으면 UsernameNotFoundException을 Throw 하게 되어있는데
BadCredentialsException으로 리턴되고 있었다. 왜 그런지 궁금하여 분석 후 포스팅한다.
● 분석
AuthenticationManager에 UserDetailsService를 등록해주면 기본적으로 DaoAuthenticationProvider가 등록해준 UserDetailsService를 가지고 있게 된다.
그리고 AbstractUserDetailsauthenticationProvider의 authenticate함수가 실행되면서 DaoAuthenticationProvider에게 요청 온 로그인 정보를 가지고 인증 절차를 하라고 지시한다.
그러면 UserDetailsService의 loadUserByUsername가 실행되는데 이때 UsernameNotFoundException에러가 발생되면
AbstractUserDetailsauthenticationProvider의 hideUserNotFoundException(Boolean)를 확인하여 UserNotFoundException을 보여줄 건지 안 보여준다면 BadCredentialsException을 내보내게 되어있다.
● 확인
소스로 확인해보자.
AbstractUserDetailsauthenticationProvider.java
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
() -> this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
"Only UsernamePasswordAuthenticationToken is supported"));
String username = determineUsername(authentication);
boolean cacheWasUsed = true;
UserDetails user = this.userCache.getUserFromCache(username);
if (user == null) {
cacheWasUsed = false;
try {
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
}
catch (UsernameNotFoundException ex) {
this.logger.debug("Failed to find user '" + username + "'");
if (!this.hideUserNotFoundExceptions) {
throw ex;
}
throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
}
try {
this.preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
}
catch (AuthenticationException ex) {
if (!cacheWasUsed) {
throw ex;
}
// There was a problem, so try again after checking
// we're using latest data (i.e. not from the cache)
cacheWasUsed = false;
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
this.preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
}
this.postAuthenticationChecks.check(user);
if (!cacheWasUsed) {
this.userCache.putUserInCache(user);
}
Object principalToReturn = user;
if (this.forcePrincipalAsString) {
principalToReturn = user.getUsername();
}
return createSuccessAuthentication(principalToReturn, authentication, user);
}
AbstractUserDetailsauthenticationProvider에서 retrievUser 함수를 실행하게 되는데 DaoAuthenticationProvider의retrievUser를 수행하게 된다.
DaoAuthenticationProvider.java
@Override
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
prepareTimingAttackProtection();
try {
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
if (loadedUser == null) {
throw new InternalAuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
catch (UsernameNotFoundException ex) {
mitigateAgainstTimingAttack(authentication);
throw ex;
}
catch (InternalAuthenticationServiceException ex) {
throw ex;
}
catch (Exception ex) {
throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
}
}
DaoAuthenticationProvider에서 this.getUserDetailsService().loadUserByUsername(username); 서비스단이 실행되고
UsernameNotFoundException에러가 발생!! 그리고 AbstractUserDetailsauthenticationProvider로 Throw하게 된다.
AbstractUserDetailsauthenticationProvider에서는 hideUserNotFoundException를 체크 후 Exception을 처리하게 된다.!!
'Java > SpringBoot' 카테고리의 다른 글
[SpringBoot] Log4j2 기본 사용법 (0) | 2022.03.07 |
---|---|
[SpringBoot] SOP, CORS 이야기 (0) | 2022.02.03 |
[SpringBoot] Security 인증 절차 시 DB Access 여러번 일어나는 이슈. (0) | 2022.01.12 |
[SpringBoot] 종속성 순환 에러 트러블슈팅 (0) | 2021.12.30 |
[SpringBoot] StereoType, @Component과 @Bean 차이점 (0) | 2021.12.30 |