Spring Webflux 및 MVC Security환경 비교

2023. 7. 15. 14:05Spring/webflux

728x90

개인적으로 느끼기에는 구조 자체는 MVC 환경 Security와 비슷하다.

허나 반환 형식이랑 체인 설정법만 조금씩 다른 느낌이다.

 

 

SecurityConfig

MVC 환경

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class SecurityConfig {
    JwtAuthenticationFilter jwtAuthenticationFilter;

    @Bean
    public SecurityFilterChain configure(HttpSecurity http) throws Exception {
        return http.authorizeHttpRequests(this::match)
                .addFilterAfter(jwtAuthenticationFilter, BasicAuthenticationFilter.class)
                .cors(c -> c.configurationSource(corsConfigurationSource()))
                .csrf(AbstractHttpConfigurer::disable)
                .build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedMethod("*");
        config.addAllowedOrigin("https://*");
        config.addAllowedHeader("*");
        config.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }
    
     public AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry
            match(
                    AuthorizeHttpRequestsConfigurer<HttpSecurity>
                                    .AuthorizationManagerRequestMatcherRegistry
                            r) {
        return r.anyRequest().authenticated();
    }
}

Webflux 환경

 

@Configuration
@EnableWebFluxSecurity
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
@RequiredArgsConstructor
public class WebfluxSecurityConfig {
    WebfluxAuthFilter webfluxAuthFilter;

    @Bean
    public SecurityWebFilterChain configure(ServerHttpSecurity http) {
        return http.authorizeExchange(getAuthorizeExchangeSpecCustomizer())
                .csrf(ServerHttpSecurity.CsrfSpec::disable)
                .addFilterBefore(webfluxAuthFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                .cors(c -> c.configurationSource(corsConfigurationSource()))
                .build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedMethod("*");
        config.addAllowedOrigin("https://**");
        config.addAllowedHeader("*");
        config.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }

    private Customizer<ServerHttpSecurity.AuthorizeExchangeSpec>
            getAuthorizeExchangeSpecCustomizer() {
        return r -> r.pathMatchers("/**").permitAll();
    }
}

차이점

  MVC Webflux
annotation @EnableWebSecurity 사용 @EnableWebFluxSecurity 사용
cors ConfigurationSource cors 하위 패키지 요소 사용 cors.reactive 하위 패키지 요소 사용
SecurityFilterChain HttpSecurity.authorizeHttpRequest  ServerHttpSecurity.authorizeExchange

생각보다 두 구조가 비슷한 것을 확인할 수 있다

Filter

Fliter 구조 또한 비슷한 것을 확인할 수 있다.

개인적으로 다양한 레퍼런스를 확인해봤을 때, MVC 환경에서는 다양한 Filter을 extends 해서 사용하는 경향이 있고, Webflux 환경에서는 WebFilter를 직접 구현해서 사용하는 경향이 있는 것 같다

아래 예시는 @Override 부분 구조만 확인해보면 좋을듯하다.

 

MVC 환경

@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    JwtExtractor extractor;
    FilterExclusionValidator exclusionValidator;

    @Override
    protected void doFilterInternal(
            HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        final String token = request.getHeader("Authorization");
        final AuthUser auth = new AuthUser(extractor.extract(token));
        SecurityContextHolder.getContext().setAuthentication(auth);
        filterChain.doFilter(request, response);
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        return exclusionValidator.validate(request.getServletPath());
    }
}

Webflux 환경

@Component
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
@RequiredArgsConstructor
public class WebfluxAuthFilter implements WebFilter {
    JwtExtractor jwtExtractor;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        final JwtPayload payload = getJwtPayload(exchange.getRequest());
        return chain.filter(exchange)
                .contextWrite(
                        ReactiveSecurityContextHolder.withAuthentication(new AuthUser(payload)));
    }

    private JwtPayload getJwtPayload(final ServerHttpRequest request) {
        final String token = request.getHeaders().getFirst("Authorization");
        return jwtExtractor.extract(token);
    }
}

차이점

  MVC Webflux
filter 반환 형태 void Mono<Void>
다음 필터 실행 filterChain.doFilter(req,res) WebFilterChain.filter(exchange)
SecurityContext 접근 및 Auth설정 SCH.getContext().setAuth...(auth) RSCH.withAuthentication(auth)

보시다시피 webflux 환경에서는 어떠한 흐름을 반환하는(Publisher를 반환하는) 구조로 되어있다.

 

Controller단에서 Principal 가져오기

 

MVC 환경

@RestController
public class Controller {
    
    @GetMapping
    public void getUser(Authentication auth) {
        auth.getName();
    }
}

Webflux 환경

@RestController
public class WebfluxController {
  
    @GetMapping
    public Mono<void> match(Mono<Authentication> auth) {
        return matchService.subscribe(auth.map(Authentication::getName));
    }
}

반환 형태가 기존 Authentication에서 Mono로 wrapping 한 것 외에 차이점이 없다.

728x90