1. 목적

  • msa 프로젝트를 하면서 api에 대한 인가처리가 필요했다.
  • 인가처리를 위해서 구상한 아키텍쳐는 kong g/w를 사용하여 lua로 plugin을 작성했다. (plugin to 인가모듈)
  • 진행 과정에서 인가처리를 위한 Kong 사용과 lua를 사용하는 것에 대한 불편함이 느껴졌다. 나혼자 하면 괜찮겠지만 팀원들에게 민폐다.
  • 그러다가 재미난 글을 보게되었다. 사이드카 프록시로 구현한 서비스 인증
  • 그대로 구현해본다.

2. 글을 통해 알 수 있는 점

  • webflux로 구현하셨다. mvc도 가능한데, @RequestMapping 사용에 약간의 문제가 있다. (메소드 별로 구현해야했음)
  • 끝..

3. 구현

  1. proxy의 본체는 그대로 구현한다. 요청 정보를 target 서비스로 그대로 넘겨준다.
    @RequestMapping(value = "/**")
    public Mono<ResponseEntity<byte[]>> proxy(ServerHttpRequest request, ProxyExchange<byte[]> proxy) {
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpRequest(request);
        String modifiedUri = builder.scheme(targetScheme).host(targetHost).port(targetPort).build(false)
                .toUriString();
        return proxy.uri(modifiedUri).forward(r -> {
            HttpHeaders filtered = filter(r.getHeaders());
            return ResponseEntity.status(r.getStatusCode()).headers(filtered).body(r.getBody());
        });
    }
  1. spring security 생각보다 여기서 고생을 좀 많이..했다. (boot 3.1 환경에서 작동합니다. webflux + boot3를 처음 시도한 사람)

@Configuration
@RequiredArgsConstructor
@EnableWebFluxSecurity
@Slf4j
public class SecurityConfig {

    private final CustomAuthenticationConverter customAuthenticationConverter;
    private final CustomAuthorizationManager customAuthorizationManager;

    //whitelist urls
    private static final String[] AUTH_WHITELIST = {
            "/*/swagger-ui/**",
            "/*/v3/api-docs/**",
			....
    };

    @Bean
    public SecurityWebFilterChain filterChain(ServerHttpSecurity http) throws Exception {
        ReactiveAuthenticationManager authenticationManager = Mono::just;
        AuthenticationWebFilter authenticFilter = new AuthenticationWebFilter(authenticationManager);
        authenticFilter.setServerAuthenticationConverter(customAuthenticationConverter);

        http
                .csrf(csrf -> csrf.disable())
                .headers(c -> c.frameOptions(f -> f.disable()).disable())
                .httpBasic(h -> h.disable())
                .securityContextRepository(NoOpServerSecurityContextRepository.getInstance()) //session STATELESS
                .authorizeExchange(exchanges -> {
                            exchanges.pathMatchers(AUTH_WHITELIST).permitAll();
                            exchanges.pathMatchers("/**").access(customAuthorizationManager);
                        }
                )
                .addFilterAt(authenticFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                .exceptionHandling(r-> {
                    r.accessDeniedHandler(new CustomAccessDeniedHandler());
                    r.authenticationEntryPoint(new CustomAuthenticationEntryPoint());
                })
        ;

        return http.build();
    }
}
  1. 인가처리 CustomAuthorizationManager
  • ReactiveAuthorizationManager의 check method를 Override
  • 해당 method에서 권한이 일치하면 AuthorizationDecision 를 true로 아닐 경우 false로 처리
  • 필요한 인가 처리에 맞게 비즈니스 로직을 작성
  • 어플리케이션 main이 작동하면 바로 인가데이터를 캐시로 저장해서 올려두고 request 정보와 대조하여 처리

0개의 댓글