인증 흐름 이해

Authentication Flow

  1. Client에서 로그인 요청
  2. UsernamePasswordAuthenticationFilter에서 Id + Password를 담은 인증 객체(Authentication)를 생성한다.
  3. AuthenticationManager에게 인증객체를 넘기며 인증 처리를 위임한다.
    → 내부에 한 개 이상의 Provider를 담은 List를 가지고 있고 그 List에서 적절한 Provider를 찾는다.
  4. 인증 관리자(AuthenticationManager)는 적절한 Provider(AuthenticationProvider)에게 인증 처리를 위임한다.
  5. 해당 Provider는 input 정보(id, password)를 가지고 실제 인증 처리 역할을 한다.
    loadUserByUsername(username) 메서드를 호출해서 유저 객체를 요청
    UserDetailsService 인터페이스에게 loadUserByUsername(username) 요청
    • RepositoryfindById() 메서드로 유저 객체를 조회
    • 만약, 해당 유저객체가 존재하지 않으면 UsernameNotFoundException이 발생하고 UsernamePasswordAuthenticationFilter에서 예외를 처리한다.
      FailHandler()에서 후속 처리
    • 존재한다면 UserDetails 타입으로 반환된다. (Member 객체도 UserDetails 객체로 변환되어 반환)
  6. 인증 관리자(AuthenticationManager)는 이제 Password 검증을 시작한다.
    → 인증 객체의 password와 반환받은 UserDetails의 password를 비교
    • 일치하지 않을 경우 BadCredentialException 발생 후 인증 실패
      → 성공한 인증객체(UserDetailsauthorities를 담은 인증 후 토큰 객체 Authentication)를 UsernamePasswordAuthenticationFilter에 전달
  7. SecurityContext에 저장한다.
  8. 이후 전역적으로 SecurityContextHolder에서 인증 객체를 사용 가능하게 한다.

인증 관리자

AuthenticationManager

  • AutenticationManager는 인터페이스, ProviderManager는 구현체
  • AuthenticationProvider 목록 중에서 인증 처리 요건에 맞는 AuthenticationProvider 를 찾아 인증처리를 위임한다.
  • 적절한 Provider를 찾지 못하면, 부모 ProviderManager 를 설정하여 AuthenticationProvider 를 계속 탐색 할 수 있다

인증 관리자(AuthenticationManager)는 필터로부터 인증 처리를 지시 받으면 가지고 있는 인증 처리자(AuthenticationProvider)들 중에서 현재 인증처리를 할 수 있는 Provider에게 인증 처리를 위임하여 인증 처리 수행 후 인증이 성공한다면 반환받은 인증 객체를 필터로 전달해준다.

만약, 적절한 Provider를 찾지 못한다면, 자신의 부모 객체에 저장된 ProviderManager도 검색하여 해당 인증을 처리할 수 있는 Provider가 있으면 인증 처리를 위임하여 반환한다.

인증 처리자

AuthenticationProvider

AuthenticationProvider는 인증 처리 시 가장 핵심적인 역할을 한다.

  • AuthenticationProvider는 인터페이스이다. 다음 두 개의 메서드를 제공한다.
    • authenticate(authentication) : 실제적인 인증 처리를 위한 검증 메서드
    • supports(authentication) : 인증처리가 가능한 Provider 인지 검사하는 메서드
  • 두 개의 메서드는 사용자가 입력한 아이디와 패스워드가 들어간 authentication 객체를 가지고 로직을 수행한다.
  1. 아이디 검증
  • UserDetailsService 인터페이스에서 인증을 요구하는 사용자 정보를 조회한다.
  • 존재할 경우 UserDetails 타입으로 반환
  • 존재하지 않을 경우 UserNotFoundException 발생
  1. 패스워드 검증
  • 반환된 UserDetails에 저장된 password와 로그인 시 입력한 패스워드(authentication.password)가 일치하는지 비교한다.
  • 일치하지 않을 경우 BadCredentialException 발생
  • 일반적으로 패스워드를 저장할 때 Password Encoder를 이용해 암호화하여 저장하기 때문에 해당 클래스(PasswordEncoder)를 이용해 두 암호를 비교한다.
  1. 추가 검증
  • 추가적으로 사용자가 정의한 검증 조건 검증
  • authenticate(authentication) 에서 검증이 모두 성공하면 최종적으로 인증 객체(Authentication(user, authorities))를 생성하여 AuthenticationManager에 전달한다.

인가 개념 및 필터 이해

Authorization - 인가 처리

당신에게 무엇이 허가 되었는지 증명하는 것

인증된 사용자가 특정 자원에 접근하고자 할 때 접근할 자격이 되는지 증명하는 것을 인가(Authorization)이라 한다.

  • 사용자가 특정 자원에 접근하고자 요청(Request)을 하면 그 사용자가 인증을 받았는지 확인한다.
  • 인증을 받은 사용자라면 해당 사용자의 자격(권한)이 해당 자원에 접근할 자격이 되는지 확인한다.
    • 위 그림에서 사용자는 Manager이기 때문에 인증된 상태라 볼 수 있다.
    • 인가 처리 부분에서 사용자는 Manager이기 때문에 User Section, Manager Section 까지는 접근이 가능하다.
    • Admin Section의 Resources는 권한 부족으로 접근이 허용되지 않는다.

스프링 시큐리티가 지원하는 권한 계층

  1. 웹 계층
  • URL 요청에 따른 메뉴 혹은 화면 단위의 레벨 보안
    /user 경로로 자원 접근을 할 때 그 자원에 설정된 권한 (ROLE_USER)과 사용자가 가진 권한을 서로 심사해서 결정하는 계층
  1. 서비스 계층
  • 화면 단위가 아닌 메소드 같은 기능 단위의 레벨 보안
    user()라는 메소드에 접근하고자 할 때 해당 메소드에 설정된 권한과 사용자가 가진 권한을 서로 심사해서 결정하는 계층
  1. 도메인 계층(Access Control List, 접근 제어 목록)
  • 객체 단위의 레벨 보안
    → 객체(user)를 핸들링 하고자 할 때 도메인에 설정된 권한과 사용자가 가진 권한을 서로 심사해서 결정하는 계층

FilterSecurityInterceptor - 인가 처리 담당 필터

  • 마지막에 위치한 필터로써 인증된 사용자에 대해 특정 요청의 승인/거부 여를 최종적으로 결정
  • 인증객체 없이 보호자원에 접근을 시도할 경우 AuthenticationException 발생
  • 인증 후 자원에 접근 가능한 권한이 존재하지 않을 경우 AccessDeniedException 발생
  • 권한 제어 방식 중 HTTP 자원의 보안을 처리하는 필터
  • 권한 처리를 AccessDecisionManager에게 맡김

Flow

  1. 사용자가 자원 접근(Request)
  2. FilterSecurityInterceptor에서 요청을 받아서 인증 여부를 확인한다.
  • 인증 객체를 가지고 있는지 확인한다.
  • 인증 객체가 없으면(null) AuthenticationException 발생
  • ExceptionTranslationFilter에서 해당 예외를 받아 다시 로그인 페이지로 이동하는 등의 후처리를 해준다.
  1. 인증 객체가 있을 경우 SecurityMetadataSource는 자원에 접근하기 위해 설정된 권한 정보를 조회해서 전달해준다.
  • 권한 정보를 조회
  • 권한 정보가 없으면(null) 권한 심사를 하지 않고 자원 접근을 허용한다.
  1. 권한 정보가 있을 경우 AccessDecisionManager에게 권한 정보를 전달하여 위임한다.
    AccessDecisionManager는 최종 심의 결정자이다.
  2. AccessDecisionManager가 내부적으로 AccessDecisionVoter(심의자)를 통해 심의 요청을 한다.
  3. 반환된 승인/거부 결과를 가지고 사용자가 해당 자원에 접근이 가능한지 판단한다.
  • 접근이 거부되었을 경우 AccessDeniedException이 발생
  • ExceptionTranslationFilter에서 해당 예외를 받아 후처리를 해준다.
  1. 접근이 승인되었을 경우 자원 접근이 허용된다.

인가 결정 심의자

AccessDecisionManager

  • 인증 정보, 요청 정보, 권한 정보를 이용해서 사용자의 자원 접근을 허용할 것인지 거부할 것인지를 최종 결정하는 주체
  • 여러 개의 Voter 들을 가질 수 있으며 Voter 들로부터 접근 허용, 거부, 보류에 해당하는 각각의 값을 리턴받고 판단 및 결정
  • 최종 접근 거부 시 예외 발생

접근 결정의 세 가지 유형

  • AffirmativeBased : OR 연산자와 비슷한 논리
    → 여러 개의 Voter 클래스 중 하나라도 접근 허가로 결론을 내면 접근 허가로 판단한다.
  • ConsensusBased : 다수결
    → 다수표(승인 및 거부)에 의해 최종 결정을 판단한다.
    → 동수일 경우 기본은 접근 허가이지만, allowIfEqualGrantedDeniedDecisions를 false로 설정할 경우 접근 거부로 결정된다.
  • UnanimousBased : AND 연산자와 동일한 논리
    → 모든 Voter가 만장일치로 접근을 승인해야 하며 그렇지 않은 경우 접근을 거부한다.

AccessDecisionVoter

  • 판단을 심사하는 것(위원)
  • Voter가 권한 부여 과정에서 판단하는 자료
    • Authentication : 인증 정보
    • FilterInvocation : 요청 정보(antMatcher("/user"))
    • ConfigAttributes : 권한 정보(hasRole("USER"))
  • 결정 방식
    • ACCESS_GRANTED : 접근 허용(1)
    • ACCESS_DENIED : 접근 거부(-1)
    • ACCESS_ABSTAIN: 접근 보류(0)
      • Voter가 해당 타입의 요청에 대해 결정을 내릴 수 없는 경우

Flow

  1. FilterSecurityInterceptorAccessDecisionManager에 인가 처리 위임
  2. AccessDecisionManager는 자신이 가지고 있는 Voter들에게 정보 (decide(authentication, object, configAttributes))를 전달한다.
  3. Voter들은 정보들을 가지고 권한 판단을 심사한다.
  4. 승인, 거부, 보류 결정 방식을 반환하면 AccessDecisionManager에선 반환받은 결정 방식을 통해 후처리를 한다.
  • 승인: FilterSecurityInterceptor에 승인 여부 반환
  • 거부: AccessDeniedException 예외를 ExceptionTranslationFilter로 전달

References

profile
Step by step goes a long way.

0개의 댓글