마이크로서비스가 10개인데, 각 서비스마다 JWT 검증 로직을 넣어야 하나요? 인증을 한 곳에서 처리할 수는 없을까요?

개념 정의

MSA에서 API Gateway 는 모든 외부 요청의 진입점입니다. 인증(이 사용자가 누구인가?)을 Gateway에서 처리하고, 인가(이 사용자가 이 작업을 할 수 있는가?)는 각 서비스에서 처리하는 패턴이 가장 일반적입니다.

전체 구조

역할담당처리 내용
인증API GatewayJWT 토큰 유효성 검증, 만료 확인, 서명 확인
** 인가**각 마이크로서비스사용자 권한(ROLE) 기반 접근 제어
** 사용자 정보 전달**Gateway → 서비스헤더에 userId, roles 등 전달

패턴 1: Gateway에서 토큰 검증 + 헤더 전달

가장 많이 쓰이는 패턴입니다.

Gateway 설정 (Spring Cloud Gateway):

JAVA
@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();
        }

토큰 검증 후 사용자 정보를 내부 헤더로 전달하면 각 마이크로서비스에서 직접 토큰을 파싱할 필요가 없습니다.

JAVA
        // 토큰에서 사용자 정보 추출 → 헤더로 전달
        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가 토큰을 검증한 후, 사용자 정보를 ** 내부 헤더 **로 전달합니다. 각 마이크로서비스는 이 헤더만 읽으면 됩니다.

** 마이크로서비스에서 사용자 정보 읽기 **:

JAVA
@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를 검증합니다.

JAVA
// 각 마이크로서비스의 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를 우회해서 다른 사용자로 위장할 수 있습니다.

PLAINTEXT
# 공격자가 직접 마이크로서비스에 요청
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에서 "누구인지" 확인하고, 각 서비스에서 "뭘 할 수 있는지" 결정합니다. 인증은 중앙에서, 인가는 분산해서.

댓글 로딩 중...