Spring Security (2) - Authentication 2

spaghetti·2024년 7월 24일

Spring Security

목록 보기
3/7

AuthenticationManager

이전에 UsernamePasswordAuthenticationFilter 에서 Authentication 객체를 만들기위해서 AuthenticationManger에게 전달한다고 하였다.
아키텍처 부분에서 설명했는데 AuthenticationManger는 인터페이스로 이를 구현한 ProviderManger가 가장 흔히 쓰인다고 했다. 해당 구현체에서 오버라이드한 메서드를 살펴보자.

AuthenticationProvider

providerManger는 코드를 보면 for문을 통해 여러개의 providers들을 돌면서 인증을 한다. 이를 보면 여러개의 provider 목록들로 인증을 진행할 수 있다는 것을 알 수 있다.
위의 코드에서 provider는 List<AuthenticationProvider> providers 의 목록에서 가져오는데, AuthenticationProvider도 인터페이스로 여러 구현체를 가지고 있다. 우선 공식문서에서 예시로 든 username/password 를 기반으로한 구현체인 DaoAuthenticationProvider를 살펴보고자 한다.
참고로 해당 클래스는 AbstractUserDetailsAuthenticationProvider 인터페이스를 구현한 클래스다

블록된 코드를 살펴보면 우선 로그인한 정보(user)가 없는 경우에 retrieveUser를 호출한다. 이 메서드에서 DB에 있는 유저 정보를 읽어오게 되는데, 없으면 UsernameNotFoundException을 던지는 것을 볼 수 있다.

DaoAuthenticationProvider에서 실제로 구현한 메서드를 살펴보면 아래와 같이 UserDetailsService의 loadUserByUsername을 호출하고 있다. 그래서 UserDetailService 인터페이스를 직접 구현한 userService를 만들어서 loadUserByUsername 오버라이딩하고 프로젝트의 DB에서 조회하는 로직을 구현하게된다.

public class MyUserService implements UserDetailsService {
  @Override
  public UserDetails loadUserByUsername(
      String username
  ) throws UsernameNotFoundException {
	//DB를 조회하여 User정보를 가져오는 로직
    
  }

loadUserByUsername 는 반환할때 UserDetails를 반환한다. 그래서 DB에서 유저정보를 가져오고 객체로 매핑할때 UserDetails 인터페이스를 구현한 객체를 만들어서 매핑 시켜준다.

🔎 MyUserService 를 Config에 등록하지 않아도 동작하는 이유?
spring security에서 내가 구현한 서비스를 빈으로 등록하면 자동으로 감지하여 인증 과정에서 사용한다고 한다. DaoAuthenticationProvider에서 UseretailService를 필요로 하기때문에 빈이 존재했을때 해당 클래스에서 설정이 이루어진다.

PasswordEncoder

spring security는 암호화된 비밀번호를 저장하고, 로그인 시 해당 암호화된 비밀번호를 비교하여 체크한다.
DaoAuthenticationProvider 에서 유저정보를 읽어온 후에 additionalAuthenticationChecks 메서드를 호출하는 것을 볼 수 있는데 이때 DB에서 읽어온 암호화된 비밀번호와 로그인시 입력한 비밀번호를 체크하여 해당 비밀번호가 일치하는지 확인한다.

보면 loadUserByUsername 에서 읽어온 UserDetails와 로그인시에 정보를 입력하여 생성했던 토큰값을 파라미터로 받고 있다.

암호화객체도 여러가지가 존재하는데 security의 공식 문서에서 Password Storage History 읽어보면 상세하게 알 수 있다.

security 5 버전에서는 기본적으로 NoOpPasswordEncoder였으며 일반 텍스트 비밀번호가 필요했지만 보안적인 문제로 BCryptPasswordEncoder를 사용하라고 한다.

문서를 좀 읽어보니(영어이슈) 최근 버전에 크게 바뀐것이라 이전 버전부터 계속 시큐리티를 사용하고 있는 많은 프로젝트에서 사용했을 레거시 방식의 암호화의 경우에도 지원해야 한다. 때문에 security에서는 기본적으로 DelegatingPasswordEncoder 를 사용한다고 한다. 이 클래스를 통해 어떤 암호 알고리즘을 사용했는지 파악하여 해당 암호화 구현체에게 넘겨준다고 한다. 내가 특별히 사용해야 할 암호화 방식이 있다면 인터페이스를 통해 구현체를 만들고 빈에 등록할 수 있는 방법을 지원하고 있다.
만약 이전 프로젝트가 없더라도 특별히 암호화 알고리즘을 적용하지 않으면 security에서 권장하는 최신 암호화 알고리즘을 기본적으로 사용한다고 한다.

profile
개발 그렇게 하는거 아닌데의 그렇게를 맡고있습니다

0개의 댓글