1. 소개
이 사용방법(예제)에서는 AuthenticationManagerResolver 를 소개 하고 기본 및 OAuth2 인증 흐름에 사용하는 방법을 보여줍니다.
2. AuthenticationManager 란 무엇입니까 ?
간단히 말해서, AuthenticationManager 는 인증을 위한 주요 전략 인터페이스입니다.
입력 인증의 주체가 유효하고 확인된 경우 AuthenticationManager#authenticate 는 인증된 플래그가 true 로 설정된 인증 인스턴스를 반환 합니다 . 그렇지 않고 Security 주체가 유효하지 않으면 AuthenticationException 이 발생 합니다. 마지막 경우 결정할 수 없으면 null 을 반환합니다.
ProviderManager 는 AuthenticationManager 의 기본 구현입니다 . 인증 프로세스를 AuthenticationProvider 인스턴스 List에 위임합니다 .
SecurityFilterChain 빈 을 생성하면 전역 또는 로컬 AuthenticationManager 를 설정할 수 있습니다 . 로컬 AuthenticationManager 의 경우 HttpSecurity 를 통해 AuthenticationManagerBuilder 에 액세스 하는 AuthenticationManager 빈 을 만들 수 있습니다 .
AuthenticationManagerBuilder 는 AuthenticationManager 를 빌드하기 위해 UserDetailService , AuthenticationProvider 및 기타 의존성의 설정을 용이하게 하는 도우미 클래스입니다.
전역 AuthenticationManager 의 경우 AuthenticationManager 를 빈으로 정의해야 합니다 .
3. 왜 AuthenticationManagerResolver 인가?
AuthenticationManagerResolver 를 사용하면 Spring이 컨텍스트별로 AuthenticationManager 를 선택할 수 있습니다. 버전 5.2.0의 Spring Security에 추가 된 새로운 기능 입니다.
public interface AuthenticationManagerResolver<C> {
AuthenticationManager resolve(C context);
}
AuthenticationManagerResolver#resolve 는 일반 컨텍스트를 기반으로 하는 AuthenticationManager 의 인스턴스를 반환할 수 있습니다 . 즉 , 클래스에 따라 AuthenticationManager 를 해결하려는 경우 클래스를 컨텍스트로 설정할 수 있습니다 .
Spring Security는 인증 흐름에서 HttpServletRequest 및 ServerWebExchange 를 컨텍스트로 사용하여 AuthenticationManagerResolver 를 통합했습니다 .
4. 사용 시나리오
실제로 AuthenticationManagerResolver 를 사용하는 방법을 살펴보자 .
예를 들어 직원과 고객이라는 두 그룹의 사용자가 있는 시스템을 가정합니다. 이 두 그룹에는 특정 인증 논리가 있으며 별도의 데이터 저장소가 있습니다. 또한 이러한 그룹 중 하나의 사용자는 관련 URL만 호출할 수 있습니다.
5. AuthenticationManagerResolver 는 어떻게 작동합니까?
AuthenticationManager 를 동적으로 선택해야 하는 모든 곳에서 AuthenticationManagerResolver 를 사용할 수 있지만 이 사용방법(예제)에서는 기본 제공 인증 흐름에서 사용하는 데 관심이 있습니다.
먼저 AuthenticationManagerResolver 를 설정한 다음 기본 및 OAuth2 인증에 사용합니다.
5.1. AuthenticationManagerResolver 설정
Security 구성을 위한 클래스를 만드는 것부터 시작하겠습니다.
@Configuration
public class CustomWebSecurityConfigurer {
// ...
}
그런 다음 고객을 위해 AuthenticationManager 를 반환하는 메서드를 추가해 보겠습니다 .
AuthenticationManager customersAuthenticationManager() {
return authentication -> {
if (isCustomer(authentication)) {
return new UsernamePasswordAuthenticationToken(/*credentials*/);
}
throw new UsernameNotFoundException(/*principal name*/);
};
}
직원을 위한 AuthenticationManager 는 논리적으로 동일하지만 isCustomer 를 isEmployee 로 바꿉니다 .
public AuthenticationManager employeesAuthenticationManager() {
return authentication -> {
if (isEmployee(authentication)) {
return new UsernamePasswordAuthenticationToken(/*credentials*/);
}
throw new UsernameNotFoundException(/*principal name*/);
};
}
마지막으로 요청 URL에 따라 확인 하는 AuthenticationManagerResolver 를 추가해 보겠습니다.
AuthenticationManagerResolver<HttpServletRequest> resolver() {
return request -> {
if (request.getPathInfo().startsWith("/employee")) {
return employeesAuthenticationManager();
}
return customersAuthenticationManager();
};
}
5.2. 기본 인증의 경우
AuthenticationFilter 를 사용 하여 요청별로 AuthenticationManager 를 동적으로 해결할 수 있습니다. AuthenticationFilter 는 버전 5.2에서 Spring Security에 추가되었습니다.
Security 필터 체인에 추가하면 일치하는 모든 요청에 대해 먼저 인증 개체를 추출할 수 있는지 여부를 확인합니다. 그렇다면 적절한 AuthenticationManager 에 대해 AuthenticationManagerResolver 에 요청 하고 흐름을 계속합니다.
먼저 CustomWebSecurityConfigurer 에 메서드를 추가하여 AuthenticationFilter 를 생성해 보겠습니다 .
private AuthenticationFilter authenticationFilter() {
AuthenticationFilter filter = new AuthenticationFilter(
resolver(), authenticationConverter());
filter.setSuccessHandler((request, response, auth) -> {});
return filter;
}
No-op SuccessHandler 로 AuthenticationFilter#successHandler 를 설정하는 이유는 성공적인 인증 후 리디렉션의 기본 동작을 방지하기 위한 것입니다.
그런 다음 CustomWebSecurityConfigurer 에서 SecurityFilterChain 빈 을 생성하여 이 필터를 Security 필터 체인에 추가할 수 있습니다 .
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.addFilterBefore(authenticationFilter(), BasicAuthenticationFilter.class);
return http.build();
}
5.3. OAuth2 인증의 경우
BearerTokenAuthenticationFilter 는 OAuth2 인증을 담당합니다. BearerTokenAuthenticationFilter #doFilterInternal 메소드는 요청에서 BearerTokenAuthenticationToken 을 확인하고 사용 가능한 경우 적절한 AuthenticationManager 를 확인하여 토큰을 인증합니다.
OAuth2ResourceServerConfigurer 는 BearerTokenAuthenticationFilter를 설정하는 데 사용됩니다 .
따라서 SecurityFilterChain 빈 을 생성하여 CustomWebSecurityConfigurer 에서 리소스 서버에 대한 AuthenticationManagerResolver 를 설정할 수 있습니다 .
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.oauth2ResourceServer()
.authenticationManagerResolver(resolver());
return http.build();
}
6. 반응형 애플리케이션에서 AuthenticationManager 해결
반응형 웹 응용 프로그램 의 경우 컨텍스트에 따라 AuthenticationManager 를 해결하는 개념에서 여전히 이점을 얻을 수 있습니다 . 그러나 여기에는 대신 ReactiveAuthenticationManagerResolver 가 있습니다.
@FunctionalInterface
public interface ReactiveAuthenticationManagerResolver<C> {
Mono<ReactiveAuthenticationManager> resolve(C context);
}
ReactiveAuthenticationManager 의 Mono 를 반환합니다 . ReactiveAuthenticationManager 는 AuthenticationManager 와 동일한 반응형 이므로 해당 인증 방법은 Mono 를 반환합니다 .
6.1. ReactiveAuthenticationManagerResolver 설정
Security 구성을 위한 클래스를 만드는 것부터 시작하겠습니다.
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class CustomWebSecurityConfig {
// ...
}
다음 으로 이 클래스의 고객에 대해 ReactiveAuthenticationManager 를 정의하겠습니다.
ReactiveAuthenticationManager customersAuthenticationManager() {
return authentication -> customer(authentication)
.switchIfEmpty(Mono.error(new UsernameNotFoundException(/*principal name*/)))
.map(b -> new UsernamePasswordAuthenticationToken(/*credentials*/));
}
그런 다음 직원을 위해 ReactiveAuthenticationManager 를 정의합니다.
public ReactiveAuthenticationManager employeesAuthenticationManager() {
return authentication -> employee(authentication)
.switchIfEmpty(Mono.error(new UsernameNotFoundException(/*principal name*/)))
.map(b -> new UsernamePasswordAuthenticationToken(/*credentials*/));
}
마지막으로 시나리오를 기반으로 ReactiveAuthenticationManagerResolver 를 설정합니다 .
ReactiveAuthenticationManagerResolver<ServerWebExchange> resolver() {
return exchange -> {
if (match(exchange.getRequest(), "/employee")) {
return Mono.just(employeesAuthenticationManager());
}
return Mono.just(customersAuthenticationManager());
};
}
6.2. 기본 인증의 경우
반응형 웹 애플리케이션에서 인증을 위해 AuthenticationWebFilter 를 사용할 수 있습니다 . 요청을 인증하고 Security 컨텍스트를 채웁니다.
AuthenticationWebFilter 는 먼저 요청이 일치하는지 확인합니다. 그런 다음 요청에 인증 개체가 있으면 ReactiveAuthenticationManagerResolver에서 요청에 적합한 ReactiveAuthenticationManager를 가져오고 인증 흐름 을 계속합니다.
따라서 Security 구성에서 사용자 정의된 AuthenticationWebFilter 를 설정할 수 있습니다.
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.authorizeExchange()
.pathMatchers("/**")
.authenticated()
.and()
.httpBasic()
.disable()
.addFilterAfter(
new AuthenticationWebFilter(resolver()),
SecurityWebFiltersOrder.REACTOR_CONTEXT
)
.build();
}
먼저 정상적인 인증 흐름을 방지하기 위해 ServerHttpSecurity#httpBasic 을 비활성화한 다음 사용자 지정 확인자를 전달 하는 AuthenticationWebFilter 로 수동으로 바꿉니다.
6.3. OAuth2 인증의 경우
ServerHttpSecurity#oauth2ResourceServer 를 사용하여 ReactiveAuthenticationManagerResolver 를 구성할 수 있습니다 . ServerHttpSecurity#build 는 Security 필터 체인에 해석기가 있는 AuthenticationWebFilter 인스턴스를 추가합니다 .
따라서 Security 구성에서 OAuth2 인증 필터에 대한 AuthenticationManagerResolver 를 설정해 보겠습니다 .
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
// ...
.and()
.oauth2ResourceServer()
.authenticationManagerResolver(resolver())
.and()
// ...;
}
7. 결론
이 기사에서는 간단한 시나리오 내에서 기본 및 OAuth2 인증을 위해 AuthenticationManagerResolver 를 사용했습니다.
또한 기본 및 OAuth2 인증 모두에 대해 반응형 Spring 웹 애플리케이션에서 ReactiveAuthenticationManagerResolver 의 사용법을 살펴보았습니다 .
항상 그렇듯이 소스 코드는 GitHub 에서 사용할 수 있습니다 . 우리의 반응 예제는 GitHub 에서도 사용할 수 있습니다 .