1. 소개

이 사용방법(예제)에서는 AuthenticationManagerResolver 를 소개 하고 기본 및 OAuth2 인증 흐름에 사용하는 방법을 보여줍니다.

2. AuthenticationManager 란 무엇입니까 ?

간단히 말해서, AuthenticationManager 는 인증을 위한 주요 전략 인터페이스입니다.

입력 인증의 주체가 유효하고 확인된 경우 AuthenticationManager#authenticate 는 인증된 플래그가 true 로 설정된 인증 인스턴스를 반환 합니다 . 그렇지 않고 Security 주체가 유효하지 않으면 AuthenticationException 이 발생 합니다. 마지막 경우 결정할 수 없으면 null 을 반환합니다.

ProviderManagerAuthenticationManager 의 기본 구현입니다 . 인증 프로세스를 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는 인증 흐름에서 HttpServletRequestServerWebExchange 를 컨텍스트로 사용하여 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 는 논리적으로 동일하지만 isCustomerisEmployee 로 바꿉니다 .

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반환합니다 . ReactiveAuthenticationManagerAuthenticationManager 와 동일한 반응형 이므로 해당 인증 방법은 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 에서도 사용할 수 있습니다 .

Security footer banner