티스토리 뷰


spring security는 쓰레드로컬 기반으로 동작하기 때문에 reactive 환경에서는 사용하기 힘들다. 그렇다면 reactive 환경에서는 security를 사용하지 않는걸까?


이번에는 reactive 환경에서 security를 어떻게 사용하는지 알아보자.


SecurityFilterChain

servlet stack에서는 Servlet Filter를 사용하는데, Filter Chain 중간에 DelegatingFilterProxy를 추가하고, DelegatingFilterProxy는 내부적으로 여러 개의 Security Filter를 갖는 SecurityFilterChain을 호출한다.


FilterChain내에서는 bean을 사용하기 힘들지만, SecurityFilterChain은 Spring Context를 갖기 때문에 bean에 접근이 가능하다.



SecurityWebFilterChain

reactive stack에서는 HttpWebHandlerAdapter의 WebFilter를 사용한다. HttpWebHandlerAdapter에서는 WebFilter를 등록하고, 등록된 WebFilter를 거쳐 마지막에 WebHandler로 요청이 전달되는 구조로 되어 있다.


WebFilter를 구현한 WebFilterChainProxy는 내부적으로 여러 개의 SecurityWebFilter로 구현되어 있고, servlet stack의 DelegatingFilterProxy와 동일한 역할을 한다.



ReactiveSecurityContextHolder

ReactiveSecurityContextHolder는 SecurityContextHolder와 비슷하지만, ThreadLocal 대신 context로 SecurityContext를 제공한다.(context를 사용하면 파이프라인 내부에서 어디서든 request에 접근할 수 있다.)


그렇다는 것은 context에 SecurityContext를 넣으면 쭉 전파될 것임을 보장할 수 있다는 것이다.


  • getContext: SecurityContext를 Mono 형태로 제공
  • clearContext: SecurityContext를 clear
  • withSecurityContext: securityContext를 Mono로 받고 이를 포함하는 Reactor context 반환
  • withAuthentication: security Authentication을 받고 SecurityContext를 받아서 이를 포함하는 Reactor context 반환

간단한 SecurityWebFilter 예제 코드

@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { 
    String iam = exchange.getRequest().getHeaders().getFirst("X-I-AM");
    if (iam == null) {
        final ServerHttpResponse resp = exchange.getResponse();
        resp.setStatusCode(HttpStatus.UNAUTHORIZED);
        return resp.setComplete();
    }

    Authentication authentication = new CustomAuthentication(iam); 
    return chain.filter(exchange)
            .contextWrite(ctx -> 
                ReactiveSecurityContextHolder.withAuthentication(authentication);
}
  • 헤더를 분석하여 X-I-AM에서 값을 추출하고, 값이 비었다면 401 status 반환한다.
  • 값이 비어있지 않다면 CustomAuthentication을 생성하고, ReactiveSecurityContextHolder의 withAuthentication을 사용해서 context를 주입한다.

@GetMapping("/hello")
public Mono<String> greet() {
    return ReactiveSecurityContextHolder 
            .getContext()
            .map(securityContext -> 
                securityContext.getAuthentication().getPrincipal())
            .flatMap(principal ->
                Mono.just("Hello, " + ((Principal)principal).getName()));
  • Controller에서 ReactiveSecurityContextHolder의 getContext를 이용해서 securityContext에 접근하고, 이를 이용해서 응답을 생성한다.
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함