BackEnd/Spring Security

[Spring Security] Authentication

샤아이인 2022. 8. 25.

본 글은 Spring Security docs 와 여러 블로그 들을 참고하고, 공부하면서 요약하였습니다.

 

https://docs.spring.io/spring-security/reference/servlet/authentication/architecture.html#servlet-authentication-authentication

 

Servlet Authentication Architecture :: Spring Security

ProviderManager is the most commonly used implementation of AuthenticationManager. ProviderManager delegates to a List of AuthenticationProviders. Each AuthenticationProvider has an opportunity to indicate that authentication should be successful, fail, or

docs.spring.io

위 글을 주로 참고하였습니다.

 

1. Authentication

 

Authentication은 인증이며, 당신이 누구인지 증명하는 과정이다.

 

Authentication 인증 객체는 사용자의 인증정보를 저장하는 토큰 개념이다.

 

인증시 id 와 password 를 담고 인증 검증을 위해 전달되어 사용된다.

인증 후 최종 인증 결과(UserDetails 객체, 권한 정보)를 담고 SecurityContext 에 저장되어 전역적으로 참조가 가능하다.

Authentication authentication = SecurityContexHolder.getContext().getAuthentication();

 

Authentication 인증 객체 인터페이스

1. principal: 사용자 아이디 혹은 User객체를 저장

2. credentials: 사용자 비밀번호

3. authorities: 인증된 사용자의 권한 목록

4. details: 인증 부가 정보

5. Authenticated: 인증 여부(Bool)

 

 

▶ Flow

Authentication이라는 인증객체가 클라이언트의 요청시 사용이 되고, 인증이 끝나고 활용되는과정을 살펴보자.

1) 사용자가 로그인을 시도한다 (username + password 입력 및 전달)

 

2) usernamePasswordAuthenticationFilter(인증필터)가 요청정보를 받아서 정보 추출을 하여 인증객체 (Authentication)을 생성한다. 

이때 내부에 username, password를 저장하게 된다.

 

3) AuthenticationManager가 인증객체를 가지고 인증처리를 수행한다. 이때 인증이 실패하면 예외가 발생한다.

 

4) 인증 성공 후 Authentication 인증객체를 만들어서 내부의 Principal, Credentials, Authorities, Authenticated 들을 채워넣는다.

이때 Principal 로는 찾아온 User 자체를 저장한다. 또한 보안을 위해 Credentials은 비워두기도 한다.

 

5) SecurityContextHolder객체 안의 SecurityContext에 저장한다. → 인증객체를 전역적으로 사용할 수 있게 된다.

 

2. 디버깅 해보기

로그인 시도를 해보자.

 

맨 처음 AbstractAuthenticationProcessingFilter에서 attemptAuthentication()에 걸리게 된다.

 

이후 자식 타입인 UsernamePasswordAuthenticationFilter 로 넘어와서 authRequest 라는 Token을 생성한다.

authRequest를 AuthenticationManager를 통해서 authenticate()에 인자로 넘겨준다.

 

다음은 AuthenticationManager를 상속한 ProviderManager의 authenticate()이다.

 

다음 코드를 보면 AuthenticationProvider를 찾아와서 authenticate()를 호출하게 된다.

인자로 전달된 authentication은 다음과 같다.

 

이렇게 인증과정을 거치고, AbstractUserDetailsAuthenticationProvider의 다음 메서드를 호출하게 된다.

protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
    UsernamePasswordAuthenticationToken result = 
        new UsernamePasswordAuthenticationToken(principal, 
            authentication.getCredentials(), 
            this.authoritiesMapper.mapAuthorities(user.getAuthorities())
        );
        
    result.setDetails(authentication.getDetails());
    this.logger.debug("Authenticated user");
    return result;
}

UsernamePasswordAuthenticationToken()을 통해 인증객체를 생성하게 된다.

 

UsernamePasswordAuthenticationToken 같은 경우 생성자가 2개이다.

인자 2개인 생성자는 처음 사용자가 요청을 보낼때, 즉 AuthenticationManager를 통해 인증받기 전에 사용되며

인자 3개인 생성자는 AuthenticationManager를 통해 인증에 성공한 후에 사용된다.

 

이렇게 생성된 UsernamePasswordAuthenticationToken은 다시 ProviderManager로 반환된다.

이 Token을 다시 authenticate()를 호출한 UsernamePasswordAuthenticationFilter로 반환된다.

이 Token은 다시 AbstractAuthenticationProcessingFilter 까지 전달된다. 다음 코드를 보자.

 

token을 AbstractAuthenticationProcessingFilter#successfulAuthentication()에 전달하고,

ThreadLocal에 해당 인증객체를 저장하게 된다.

 

이후 ThreadLocal에 저장되어있는 해당 인증객체는 인가 부분에서도 사용되게 된다!

 

댓글