본문 바로가기

MSA

2024-08-06(TIL) - 게이트웨이

API 게이트웨이란?

  • API 게이트웨이는 클라이언트의 요청을 받아 백엔드 서비스로 라우팅하고, 다양한 부가 기능을 제공하는 중간 서버입니다.
  • 클라이언트와 서비스 간의 단일 진입점 역할을 하며, 보안, 로깅, 모니터링, 요청 필터링 등을 처리합니다.

 

API 게이트웨이의 주요 기능

  • 라우팅: 클라이언트 요청을 적절한 서비스로 전달
  • 인증 및 권한 부여: 요청의 인증 및 권한을 검증
  • 로드 밸런싱: 여러 서비스 인스턴스 간의 부하 분산
  • 모니터링 및 로깅: 요청 및 응답을 로깅하고 모니터링
  • 요청 및 응답 변환: 요청과 응답을 변환하거나 필터링

 

라우팅 설정

server:
  port: 19091

spring:
  application:
    name: gateway-service
  main:
    web-application-type: reactive
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**
        - id: product-service
          uri: lb://product-service
          predicates:
            - Path=/products/**
        - id: auth-service
          uri: lb://auth-service
          predicates:
            - Path=/auth/**
      discovery:
        locator:
          enabled: true

service:
  jwt:
    secreat-key: "3UMzr1agWuUOpd2Ypm1BfGv3c7GLj0l3ToEYrAk/GXvQVmOyG27VVZ6tAhYHg+pubxhLYLO/ACcbpoEYo++Cjg=="


eureka:
  client:
    service-url:
      defaultZone: http://localhost:19090/eureka/

 

의존성 설정

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
	  implementation 'org.springframework.boot:spring-boot-starter-actuator'
	  implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
      implementation 'io.jsonwebtoken:jjwt:0.12.6'
	  implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
}

 

JWT 검증 필터

package com.sparta.msa_exam.gateway.filter;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.crypto.SecretKey;

@Component
@Slf4j
public class JwtAuthenticationFilter implements GlobalFilter {

    @Value("${service.jwt.secreat-key}")
    private String secretKey;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();

        if (path.equals("/auth/signIn") || path.equals("/auth/signUp")) {
            return chain.filter(exchange);
        }

        String token = getToken(exchange);
        log.info(token);

        if (token == null || !validateToken(token, exchange)) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }


        return chain.filter(exchange);
    }

    private String getToken(ServerWebExchange exchange) {
        String authorization = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (authorization != null && authorization.startsWith("Bearer ")) {
            return authorization.substring(7);
        }
        return null;
    }

    private boolean validateToken(String token, ServerWebExchange exchange) {
        try {
            SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretKey));

            Jws<Claims> claimsJws = Jwts.parser()
                    .verifyWith(key)
                    .build().parseSignedClaims(token);

            log.info("payload: {}", claimsJws.getPayload().toString());

            Claims claims = claimsJws.getBody();
            exchange.getRequest().mutate()
                    .header("X-Role", claims.get("role").toString())
                    .build();

            return true;

        } catch (Exception e) {
            log.warn("validateToken : ", e);
            return false;
        }
    }



}

 

게이트웨이 실습 흐름도