본문 바로가기

Language/Spring Security

인증 관리자 - AuthenticationManager

시큐리티 인증 / 인가 흐름도

 

  • AuthenticationManager
    • 인증 필터로부터 Authentication 객체를 전달 받아 인증 시도
      → 인증 성공 시 사용자 정보, 권한 등을 포함한 Authentication 객체 반환
    • AuthenticationManager는 여러 AuthenticationProvider들을 관리하며 AuthenticationProvider 목록을 순차적으로 순회하며 인증 요청을 처리
    • AuthenticationProvider목록 중에서 인증 처리 요건에 맞는 적절한 AuthenticationProvider를 찾아 인증 처리 위임
    • AuthenticationManagerBuilder에 의해 객체가 생성되며 주로 사용하는 구현체로 ProviderManager 제공
  • AuthenticationManagerBuilder
    • AuthenticationManager 객체를 생성 → UserDetailsService 및 AuthenticationProvider 추가 가능
    • HttpSecurity.getSharedObject(AuthenticationManagerBuilder.class) 를 통해 객체를 참조 가능

AuthenticationManager 흐름도

  • 선택적으로 부모 AuthenticationManage 구성 가능
    → 부모는 AuthenticationProvider 가 인증을 수행할 수 없는 경우에 추가적으로 탐색
    1. OAuth2 인증 요청이 들어올 경우 ProviderManager가 가진 것으로는 처리가 불가
    2. 부모격의 ProviderMnager에게 요청
    3. 부모 ProviderManager는 OAuth2를 처리할 수 있는 Provider를 가짐 → 처리
  • 일반적으로 AuthenticationProvider로 부터 null 이 아닌 응답을 받을 때 까지 차례대로 시도하며 응답을 받지 못하면 ProviderNotFoundException 발생과 함께 인증 실패

AuthenticationManager 사용 방법 1 - HttpSecurity 사용

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
    AuthenticationManager authenticationManager = authenticationManagerBuilder.build(); // build() 는 최초 한번 만 호출해야 한다
    AuthenticationManager authenticationManager = authenticationManagerBuilder.getObject(); // build() 후 getObject()로 참조

    http
            .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/","/api/login").permitAll()
                    .anyRequest().authenticated())
            .authenticationManager(authenticationManager) // 생성한 AuthenticationManager를 HttpSecurity에 저장
            .addFilterBefore(customFilter(http, authenticationManager), UsernamePasswordAuthenticationFilter.class);
    return http.build();
}

// @Bean 선언 금지 -> AuthenticationManager는 bean이 아니기 때문에 주입 불가
public CustomAuthenticationFilter customFilter(HttpSecurity http, AuthenticationManager authenticationManager) {
    CustomAuthenticationFilter customAuthenticationFilter = new CustomAuthenticationFilter(http);
    customAuthenticationFilter.setAuthenticationManager(authenticationManager);
    return customAuthenticationFilter;
}

AuthenticationManager 사용 방법 2 - 직접 생성

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
            .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/","/api/login").permitAll()
                    .anyRequest().authenticated())
            .addFilterBefore(customFilter(http), UsernamePasswordAuthenticationFilter.class);
    return http.build();
}

@Bean // @Bean 선언 가능
public CustomAuthenticationFilter customFilter(HttpSecurity http) {

    List<AuthenticationProvider> list1 = List.of(new DaoAuthenticationProvider());
    ProviderManager parent = new ProviderManager(list1);
    List<AuthenticationProvider> list2 = List.of(new AnonymousAuthenticationProvider("key"), new CustomAuthenticationProvider());
    ProviderManager authenticationManager = new ProviderManager(list2, parent);

    CustomAuthenticationFilter customAuthenticationFilter = new CustomAuthenticationFilter(http);
    customAuthenticationFilter.setAuthenticationManager(authenticationManager);

    return customAuthenticationFilter;

}

AuthenticationManager가 실질적으로 하는 역할은 별로 없음

  1. 인증필터(AuthenticationFilter)로부터 인증 토큰을 받아서 적절한 Provider를 찾아 선택하고 인증 토큰을 전달
  2. AuthenticationProvider에서 인증 절차를 수행하고 성공 시 유저 정보, 권한 등 모든 게 담긴 인증 토큰을 반환
  3. AuthenticationManager가 인증 필터에게 반환하는 중간자 역할

하지만 AuthenticationManager의 객체를 얻거나 직접 생성하는 방법을 알아야 CustomFilter를 만들어 적용할 수 있음