전공공부
[Spring Cloud Gateway] API Gateway를 활용한 실습 본문
1. What is API Gateway?
CNCF에서도 제시한 표준 MSA 구성 요소중 하나인 Gateway Pattern에 대해서 깊게 알아보려고 합니다.
DDD 기반으로 MSA를 구성 하게 된다면 필히 이런 생각이 나실 겁니다. 서비스 별로 모든 역할을 나누는 건 알겠는데 이거 어떻게 나눠서 공통 인가처리를 하지..?
저도 처음 개인 프로젝트를 진행해보면서 JWT Token 인가처리 과정을 모든 Service에서 거쳐가야 하는데 이걸 모든 서비스에 둬서 체크를 해야 하나 싶었습니다. 하지만, 마이크로서비스를 구성 할 때 이런 부분을 모두 천재적인 선배 개발자 분들이 생각하고 만드셨습니다.
API Gateway를 두고 사용하면 됩니다.
위 그림은 제가 실제로 구성중인 개인 프로젝트의 대략적인 구조를 나타낸 것 입니다. 잘 살펴보면 모든 요청은 Spring Cloud 라는 곳을 지나서 각 서비스의 요청을 받게 됩니다.
API Gateway는 모든 클라이언트의 요청이 하나의 서버로 들어와서 해당 서버에서 1차적인 처리과정 또는 거쳐가는 관문으로 생각하고 구현을 하면 됩니다.
인증 또는 로깅, LB의 역할을 수행 할 수 있습니다.
이렇게 보면 마이크로 서비스로 나눈 것은 어찌보면 왜 나누었을까 싶기도 할 겁니다. 이전의 모놀리식처럼 결국에는 Spring Cloud Gateway가 모든 요청을 감당하는 것이니까요. 하지만, 그 만큼 Spring Cloud Gateway 서버에서는 소요 시간이 길어지면 안 됩니다. 예를 들어서 DB를 매번 풀로 조회하는 로직을 Spring Cloud Gateway에 둔다던지 말 입니다.
따라서, 적절한 Scale-Out(Scaling - k8s 사용시) 이 필요합니다.
2. What is Routes, Predicates and Filters ?
Routes : 요청을 어떤 서비스로 보낼지에 대해 결정하는 기본 요소입니다. 각 라우트는 ID, 목적지 URI, 조건식(Predicate) , 필터 (Filter)로 구성 되어있으며 실제 코드로 보면 아래와 같습니다.
spring:
cloud:
gateway:
routes:
# User-service : 로그인
- id: user-service
uri: lb://USER-SERVICE #USER-Service는 유레카 서버에 등록된 application 이름이다.
predicates:
- Path=/user-service/login
- Method=POST
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/user-service/(?<segment>.*), /$\{segment}
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/user-service/**
- Method=GET
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/user-service/(?<segment>.*), /$\{segment}
- AuthorizationHeaderFilter
아래 공식문서를 참고하면 상당히 많은 옵션이 존재하고 있습니다. 그리고, 이를 보면 routes id, 목적지 uri가 기본값으로 설정 되어있다는 점을 확인 할 수 있습니다.
아래 내용중 네이버 블로그에 상세한 내용을 예시를 들어서 구성하고 있는데 이를 참고하시면 더욱 도움이 될 것 같습니다.
어찌 되었던 filters 부분에 본인이 원하는 요청을 각각 집어 넣게 됩니다.
위 예시대로라면 Cookie 헤더는 삭제하고 POST 요청으로 /user-service/ segment가 들어오면 /user-service/ 부분은 날리고 segment만 받아서 들어오게 되고 이를 유저 서버단에서는 해당 segment의 API로 받겠다는 겁니다.
가장 밑 부분은 일반 유저 서비스 요청에 대한 값으로 Custom Filter를 걸었습니다.
3. Custom Filter
Spring Cloud Gateway가 추상화 한 AbstractGatewayFilterFactory를 상속 받고 현재 객체를 Config로 우리의 로직을 추가 하기 위해서 Class 명을 타입으로 넣는다.
그렇게 되면 아래와 같이 GatewayFilter apply를 오버라이드 받게 되고 이를 우리의 필터 로직을 구현하면 되는데 아래와 같이 JWT Token을 사용하여 인가처리를 해주었다.
exchange 부분은 ServerWebExchange이고 두번째 파라미터는 GatewayFilterChain 람다 함수이다.
어찌 되었든 아래와 같이 지정하면 자세히 보면 알겠지만 token 값이 요효하지 않을때 마다 response statusCode 값을 401로 지정해서 넘겨버린다.
그리고, 이 클래스의 명을 잘 보면 'AuthorizationHeaderFilter' 인데 이는 아까 위에서 user-service/**로 가는것의 필터로 걸려있는것을 볼 수 있다.
@Component
@Slf4j
public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory<AuthorizationHeaderFilter.Config> {
private final JwtUsecase jwtUsecase;
private final SavePort savePort;
public AuthorizationHeaderFilter(JwtUsecase jwtUsecase, SavePort savePort){
super(Config.class);
this.jwtUsecase = jwtUsecase;
this.savePort = savePort;
}
public static class Config{
}
/**
* @apiNote 1. 일반적인 회원 가입 요청을 제외한 모든 요청은 이곳을 탐험한다. <br/>
* 2. 즉, 모든 요청은 이곳의 필터를 지나는데 만일, 이미 accessToken이 존재하지만 또 다른 곳에서 동일한 유저로 요청시 토큰 삭제를 하고
*<br/>
* 3. 다시 재 로그인을 할 때는 정상 로그인을 할 수 있는 방식으로 구동시켰다.
* @param config
* @return
*/
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
if(!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
String authorizationHeader = request.getHeaders().get(HttpHeaders.AUTHORIZATION).get(0);
String jwt = authorizationHeader.replace("Bearer", "");
//JWT 토큰 검증
if(!jwtUsecase.validateToken(jwt)){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
String username = jwtUsecase.getUserInfo(jwt).get("username");
savePort.putIfPresent("TOKEN",username,jwt)
.flatMap(ret -> {
if(!ret){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
}
return response.setComplete();
});
return chain.filter(exchange);
};
}
}
[여담]
아래 블로그 이외에도 상당히 많은 블로그를 참조하였지만 정리하다 보니 비슷한 개념들이 겹치고 한 블로그에서 정말로 정리 되어 있어서 이 블로그와 Spring Cloud Gateway 공식 문서 정도만 첨부드립니다.
Common application properties :: Spring Cloud Gateway
Various properties can be specified inside your application.properties file, inside your application.yml file, or as command line switches. This appendix provides a list of common Spring Cloud Gateway properties and references to the underlying classes tha
docs.spring.io
spring cloud gateway 스프링 클라우드 게이트웨이 #1 기본지식
왜 필요하게 되었는가? Proxy를 nginx 이용해서 구현하다가, 더 다양한 요구사항을 처리할 수 있는 프록시...
blog.naver.com
[Spring Cloud Gateway] Custom Filter 로 간단한 Authentiction 필터 만들고 인증 처리하기
해당 글은 Spring Cloud Gateway 의 Built-in Route로 Predicates와 Filter 조작하기) 에 의존하는 글입니다. 실습 환경을 따라하시려면 이전 글을 확인하시길 바랍니다. 현재 글에서는 모든 인증 과정 (jwt 토큰
wonit.tistory.com
'Study > Spring Boot' 카테고리의 다른 글
[분산락] Redisson 기반의 분산 락 적용기 (1) | 2024.11.09 |
---|---|
Spring Security with Async (0) | 2024.02.27 |
[Spring Cloud Netflix Eureka] 기본 개념 정리 및 사용 방법 (0) | 2024.02.13 |
[Redis] Reactive Redis 연결 및 상세 설명 (0) | 2024.02.05 |
[Project 00] Webflux + DDD (1) | 2024.02.03 |