
이전 포스팅(API Gateway 관련 Trouble Shooting)을 통해서 API Gateway 내 라우터들 중 먼저 정의된 라우터가 우선 매핑된다는 부분에 대해 알게됐습니다.
필자의 프로젝트에서 Gateway Filter Chaining 내에서 JWT 검증 과정을 거친 뒤에 내부 담긴 권한을 확인할 수 있도록 구현하였습니다.
따라서, JWT 검증 관련 필터링 작업이 선행된 후 권한 확인 필터링이 진행되어야 합니다.
이로 인해서 CustomFilter 간에 우선순위를 적용 해줘야합니다.
이번 포스팅에서는 라우트에 정의된 필터는 필터 체이닝 내에서 어떠한 우선순위로 적용되는지에 대해서 작성하게 됐습니다.
📜 참고 포스팅

공식문서를 참조하면, 내부 동작에 따라 요청 경로가 Gateway Handler Mapping에 의해서 하나의 route에 매칭되면 Filtering Web Handler는 모든 GlobalFilter 인스턴스와 route에 등록된 모든 GatewayFilter 인스턴스를 Filter Chain 내에 추가합니다.
Filter Chain을 결합한 뒤 org.springframework.core.Ordered 인터페이스로 정렬하며 순서는 getOrder() 메서드를 구현하면 원하는대로 설정이 가능합니다.
이를 참고해서 AbstractGatewayFilterFactory 상속받아서 구현한 Custom Filter 들 간의 우선순위를 적용해보겠습니다.
현재 JWT 검증 관련 필터는 AuthorizationHeaderFilter, 권한 확인용 필터는 AdminAccessFilter 입니다.
각각의 필터에 Ordered 인터페이스를 구현해줍니다.
public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory<AuthorizationHeaderFilter.Config> implements Ordered {
TokenProvider tokenProvider;
public AuthorizationHeaderFilter(Environment env, TokenProvider tokenProvider) {
super(Config.class);
this.tokenProvider = tokenProvider;
}
@Override
public int getOrder() {
return 1;
}
...
}
public class AdminAccessFilter extends AbstractGatewayFilterFactory<AdminAccessFilter.Config> implements Ordered {
TokenProvider tokenProvider;
public AdminAccessFilter(Environment env, TokenProvider tokenProvider) {
super(AdminAccessFilter.Config.class);
this.tokenProvider = tokenProvider;
}
@Override
public int getOrder() {
return 2;
}
...
}
# 상품 등록 - 관리자 권한
- id : product-service
uri: http://localhost:8082/
predicates:
- Path=/product-service/api/v1/products
- Method=PUT,POST
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/product-service/(?<segment>.*), /$\{segment}
- name: AuthorizationHeaderFilter # 테스트 시 주석처리
- name: AdminAccessFilter
구현 시, getOrdered() 메서드를 오버라이드 해줘야 하며, Spring Framework 내에서 일관되게 사용되는 규칙대로 반환값이 작을수록 우선순위가 커집니다.
위처럼 정의 후에 postman을 통해서 Product-Service로 상품 등록 요청을 보내겠습니다.
결과는 정상적으로 나온것으로 보입니다.

하지만, Ordered 구현 전에도 application.yml에 필터를 정의한 순서대로 필터링이 적용되기 때문에 확실하게 확인이 불가합니다.
이를 확인하기 위해서 필터의 위치를 바꿔서 정의한 뒤 다시 요청을 보내보겠습니다.
# 상품 등록 - 관리자 권한
- id : product-service
uri: http://localhost:8082/
predicates:
- Path=/product-service/api/v1/products
- Method=PUT,POST
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/product-service/(?<segment>.*), /$\{segment}
- name: AdminAccessFilter
- name: AuthorizationHeaderFilter # 테스트 시 주석처리
그러나, 결과는 다음과 같이 우선순위가 적용되지 않은 것처럼 나옵니다.

결론부터 보면, 이는 filter에 순서를 넣어주지 못합니다. Filter가 아닌 FitlerFactory에 순서를 넣어줬을 뿐이기 때문입니다.

공식문서에도 다시 확인해보면, GlobalFilter 간에 우선순위를 정할 때는 적용되지만, AbstractGatewayFilterFactory 간에 적용된다는 내용은 없습니다.
CustomFitler 간에 우선순위를 적용해주기 위해서는 spring-cloud-gateway가 지원하는 OrderedGatewayFilter를 사용하여 구현해주면, CustomFilter 간에도 우선순위를 구현 가능합니다.
기존의 Ordered 관련 코드를 주석처리 후 OrderedGatewayFilter를 반환하도록 구현해보겠습니다.
OrderedGatewayFilter 생성 시, 두번째 매개변수로 우선순위(int)를 입력해주면 됩니다.
@Override
public GatewayFilter apply(Config config) {
return new OrderedGatewayFilter((exchange, chain) -> {
...
},1);
}
@Override
public GatewayFilter apply(Config config) {
return new OrderedGatewayFilter((exchange, chain) -> {
...
},2);
}
# 상품 등록 - 관리자 권한
- id : product-service
uri: http://localhost:8082/
predicates:
- Path=/product-service/api/v1/products
- Method=PUT,POST
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/product-service/(?<segment>.*), /$\{segment}
- name: AdminAccessFilter
- name: AuthorizationHeaderFilter # 테스트 시 주석처리
위와 같이 정의한 뒤 상품등록 요청을 보내어 로그를 확인해보겠습니다.

정상적으로 적용된 것으로 확인됩니다.
참고로, Ordered.LOWEST_PRECEDENCE 적용 시 우선순위가 가장 낮고, Ordered.HIGHEST_PRECEDENCE 적용 시 우선순위가 가장 높습니다.