API Gateway와 Security — MSA에서 인증을 처리하는 패턴
마이크로서비스가 10개인데, 각 서비스마다 JWT 검증 로직을 넣어야 하나요? 인증을 한 곳에서 처리할 수는 없을까요?
개념 정의
MSA에서 API Gateway 는 모든 외부 요청의 진입점입니다. 인증(이 사용자가 누구인가?)을 Gateway에서 처리하고, 인가(이 사용자가 이 작업을 할 수 있는가?)는 각 서비스에서 처리하는 패턴이 가장 일반적입니다.
전체 구조
| 역할 | 담당 | 처리 내용 |
|---|---|---|
| 인증 | API Gateway | JWT 토큰 유효성 검증, 만료 확인, 서명 확인 |
| ** 인가** | 각 마이크로서비스 | 사용자 권한(ROLE) 기반 접근 제어 |
| ** 사용자 정보 전달** | Gateway → 서비스 | 헤더에 userId, roles 등 전달 |
패턴 1: Gateway에서 토큰 검증 + 헤더 전달
가장 많이 쓰이는 패턴입니다.
Gateway 설정 (Spring Cloud Gateway):
@Component
public class JwtAuthFilter implements GlobalFilter, Ordered {
private final JwtProvider jwtProvider;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = extractToken(exchange.getRequest());
if (token == null || !jwtProvider.isValid(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
토큰 검증 후 사용자 정보를 내부 헤더로 전달하면 각 마이크로서비스에서 직접 토큰을 파싱할 필요가 없습니다.
// 토큰에서 사용자 정보 추출 → 헤더로 전달
Claims claims = jwtProvider.getClaims(token);
ServerHttpRequest request = exchange.getRequest().mutate()
.header("X-User-Id", claims.getSubject())
.header("X-User-Roles", claims.get("roles", String.class))
.build();
return chain.filter(exchange.mutate().request(request).build());
}
}
Gateway가 토큰을 검증한 후, 사용자 정보를 ** 내부 헤더 **로 전달합니다. 각 마이크로서비스는 이 헤더만 읽으면 됩니다.
** 마이크로서비스에서 사용자 정보 읽기 **:
@GetMapping("/orders")
public List<Order> getOrders(
@RequestHeader("X-User-Id") String userId,
@RequestHeader("X-User-Roles") String roles) {
// Gateway가 이미 인증을 마쳤으므로 여기서는 인가만
if (!roles.contains("ORDER_READ")) {
throw new AccessDeniedException("권한 없음");
}
return orderService.findByUserId(userId);
}
패턴 2: 각 서비스에서 토큰 직접 검증
Gateway 없이, 또는 Gateway에서 인증을 하지 않는 경우 각 서비스가 직접 JWT를 검증합니다.
// 각 마이크로서비스의 SecurityConfig
http.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwkSetUri("https://auth-server/.well-known/jwks.json"))
);
| 비교 | 패턴 1 (Gateway 중앙) | 패턴 2 (각 서비스) |
|---|---|---|
| 인증 로직 위치 | Gateway 1곳 | 서비스 N곳 |
| 중복 코드 | 없음 | 각 서비스에 동일 설정 |
| 인증 정책 변경 | Gateway만 수정 | 모든 서비스 수정 |
| Gateway 장애 시 | 전체 영향 | 각 서비스 독립 |
| 내부 통신 보안 | 헤더 위조 위험 | 토큰으로 보장 |
함정 — 이걸 모르면 터진다
1. 내부 헤더 위조 공격
패턴 1에서 Gateway가 X-User-Id 헤더를 전달하는데, ** 외부에서 이 헤더를 직접 보내면** Gateway를 우회해서 다른 사용자로 위장할 수 있습니다.
# 공격자가 직접 마이크로서비스에 요청
curl -H "X-User-Id: admin" http://order-service:8080/orders
** 해결 **: 마이크로서비스를 내부 네트워크에서만 접근 가능하게 하거나, Gateway가 추가한 헤더임을 서명으로 검증합니다.
2. Gateway가 단일 장애점(SPOF)이 된다
모든 요청이 Gateway를 거치므로, Gateway가 다운되면 ** 전체 서비스가 멈춥니다 **. Gateway를 최소 2대 이상 구성하고, 헬스체크를 설정해야 합니다.
3. 토큰 만료와 갱신 타이밍
JWT 토큰이 만료되었을 때 Gateway에서 401을 반환하면, 클라이언트가 Refresh Token으로 갱신하는 동안 사용자 경험이 끊어집니다. Access Token 만료 전에 미리 갱신 하는 전략이 필요합니다.
정리
| 항목 | 권장 |
|---|---|
| 인증 위치 | API Gateway (중앙 집중) |
| 인가 위치 | 각 마이크로서비스 (비즈니스 로직과 함께) |
| 토큰 방식 | JWT (Stateless, 서비스 간 전달 용이) |
| 사용자 정보 전달 | 내부 헤더 (X-User-Id 등) |
| 필수 보안 | 내부 네트워크 격리 + Gateway 이중화 |
Gateway에서 "누구인지" 확인하고, 각 서비스에서 "뭘 할 수 있는지" 결정합니다. 인증은 중앙에서, 인가는 분산해서.