JoJun's
Jun's Devlog
JoJun's
전체 방문자
오늘
어제
  • 분류 전체보기 (51)
    • Java (16)
      • SpringBoot (12)
      • Java (4)
    • Linux (11)
      • CentOS (4)
      • 명령어(CentOS) (5)
      • Docker (2)
    • DataBase (15)
      • 튜닝 기법 (1)
      • MSSQL (7)
      • SQLD,SQLP (6)
    • 알고리즘 (4)
      • 정렬 (1)
      • 해시 (1)
      • 완전탐색 (1)
      • 탐욕법 (1)
    • 인프라 (3)
    • Web (2)
    • 개인공간 (0)

블로그 메뉴

  • 홈

공지사항

인기 글

태그

  • @Component
  • springboot
  • Dockerfile
  • 파일그룹
  • cors
  • docker
  • Java
  • MSSQL
  • 알고리즘
  • log4j2

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
JoJun's

Jun's Devlog

[SpringBoot] UserDetailsService UserNotFoundException 안되는 이유
Java/SpringBoot

[SpringBoot] UserDetailsService UserNotFoundException 안되는 이유

2022. 1. 11. 12:23
728x90
반응형

● 이슈 발생 시점

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을 처리하게 된다.!!

 

 

728x90
반응형
저작자표시 비영리 변경금지

'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
    'Java/SpringBoot' 카테고리의 다른 글
    • [SpringBoot] SOP, CORS 이야기
    • [SpringBoot] Security 인증 절차 시 DB Access 여러번 일어나는 이슈.
    • [SpringBoot] 종속성 순환 에러 트러블슈팅
    • [SpringBoot] StereoType, @Component과 @Bean 차이점
    JoJun's
    JoJun's
    JoJun's의 기술블로그

    티스토리툴바