인증 필터로 부터 Authentication 객체를 전달받아 인증을 시도, 성공시 → 사용자 정보 권한등을 포함한 완전히 채워진 Authentication 객체를 반환 (인증 받기 전과 인증 받은 후 다리 역할)
AuthenticationManager는 여러 AuthenticationProvider 관리 → AuthenticationProvider 목록을 순차적 조회, 인증 요청을 처리
AuthenticationProvider 목록 중 인증 처리 요건에 맞는 적절한 AuthenticationProvider를 찾아 인증 처리를 위임 (인증 받을 값을 다시 필터에 넘겨줌 → AuthenticationManager 역할)
AuthenticationManagerBuilder에 의해 객체가 생성, 주로 사용하는 구현체는 ProviderManager 제공
ProviderManager → 적합한 Provider를 몰색해서 처리 → (즉 중간 다리 역할만 하는 것)
ProviderManager가 갖고 있는 Authentication이 없다면 → 인증 실패 처리
⇒ AuthenticationProvider 로 부터 null 이 아닌 응답을 받을 때 까지 차례대로 시도하며 응답을 받지 못하면 ProviderNotFoundException과 함께 인증이 실패
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
AuthenticationManager authenticationManager = authenticationManagerBuilder.build();
AuthenticationManager authenticationManager = authenticationManagerBuilder.getObject();
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/login").permitAll()
// build() 는 최초 한번 만 호출해야 한다 (생성시)
// build() 후에는 getObject() 로 참조해야 한다 (가져올때)
.anyRequest().authenticated())
.authenticationManager(authenticationManager) // HttpSecurity 에 생성한 AuthenticationManager 를 저장한다
.addFilterBefore(customFilter(http, authenticationManager), // 객체를 사용 UsernamePasswordAuthenticationFilter.class);
return http.build();
}
// @Bean 으로 선언하면 안된다. AuthenticationManager 는 빈이 아니기 때문에 주입받지 못한다
public CustomAuthenticationFilter customFilter(AuthenticationManager authenticationManager) throws Exception {
CustomAuthenticationFilter customAuthenticationFilter = new CustomAuthenticationFilter();
customAuthenticationFilter.setAuthenticationManager(authenticationManager);
return customAuthenticationFilter;
}
//여기서는 아무런 작업을 하지 않는다.
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
http.formLogin(Customizer.withDefaults());
http.addFilterBefore(customFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean // @Bean 으로 선언이 가능하다
public CustomAuthenticationFilter customFilter(){
List<AuthenticationProvider> list1 = List.of(new DaoAuthenticationProvider());
ProviderManager parent = new ProviderManager(list1); //list 타입의 객체로 넘길 수 있다.
List<AuthenticationProvider> list2 = List.of(new AnonymousAuthenticationProvider("key"), new CustomAuthenticationProvider()); // 자신도 있지만 부모 역할의 parent ProviderManger 객체를 줄 수 있다.
ProviderManager authenticationManager = new ProviderManager(list2,parent);
CustomAuthenticationFilter customAuthenticationFilter = new CustomAuthenticationFilter();
customAuthenticationFilter.setAuthenticationManager(authenticationManager);
return customAuthenticationFilter;
}