Backend/spring cloud (MSA)

GateWay๋ž€?

dddzr 2025. 2. 16. 21:44

๐Ÿ“Œ 1. GateWay๋ž€? 

  • MSA (Microservices Architecture)์—์„œ Gateway๋Š” ์—ฌ๋Ÿฌ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋ฅผ ๋‹จ์ผ ์ง„์ž…์ (Entry Point)์œผ๋กœ ํ†ตํ•ฉํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ์ค‘์•™ ์ง‘์ค‘์‹ API ๋ผ์šฐํ„ฐ์ด๋‹ค.
  • ์ฃผ๋กœ API Gateway๋ผ๊ณ  ๋ถˆ๋ฆฐ๋‹ค.

 

๐Ÿ‘‰ ๋‹จ์ผ ์ง„์ž…์  ์„ค์ •

๋”๋ณด๊ธฐ
๋”๋ณด๊ธฐ

*๊ฐ ์„œ๋น„์Šค port์— ์ง์ ‘ ์ ‘๊ทผ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋„๋ก ๋ฐฉํ™”๋ฒฝ ๋“ฑ ๋„คํŠธ์›Œํฌ ์„ค์ •์œผ๋กœ ๊ฐœ๋ณ„ ์„œ๋น„์Šค ํฌํŠธ์— ๋Œ€ํ•œ ์™ธ๋ถ€ ์ ‘์†์„ ์ฐจ๋‹จ.

*Spring Boot๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, server.port=8083 server.address=127.0.0.1์„ค์ •ํ•˜์—ฌ ๋กœ์ปฌ์—์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋„๋ก ์ œํ•œํ•  ์ˆ˜ // Gateway์—์„œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์„ค์ •

exchange.getRequest().mutate().header("X-User-Roles", "ROLE_USER,ROLE_ADMIN").build();

์žˆ์Šต๋‹ˆ๋‹ค.

 

 

โœ… API Gateway์˜ ์—ญํ• 

  1. ์š”์ฒญ ๋ผ์šฐํŒ… (Request Routing): ํด๋ผ์ด์–ธํŠธ๋Š” ํ•˜๋‚˜์˜ ์—”๋“œํฌ์ธํŠธ๋งŒ ํ˜ธ์ถœํ•˜๋ฉด ๋˜๋ฉฐ, Gateway๊ฐ€ ์ด๋ฅผ ์ ์ ˆํ•œ ์„œ๋น„์Šค๋กœ ์ „๋‹ฌํ•œ๋‹ค.
  2. ์ธ์ฆ ๋ฐ ์ธ๊ฐ€ (Authentication & Authorization): ์ธ์ฆ ํ† ํฐ ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
  3. ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ (Load Balancing): ์—ฌ๋Ÿฌ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋ฐฐํฌ๋œ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค์— ๋Œ€ํ•ด ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ํŠธ๋ž˜ํ”ฝ์„ ๋ถ„๋ฐฐํ•œ๋‹ค.
  4. API ์กฐํ•ฉ (API Aggregation): ์—ฌ๋Ÿฌ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„ ํ•˜๋‚˜์˜ ์‘๋‹ต์œผ๋กœ ํด๋ผ์ด์–ธํŠธ์— ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  5. ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ๋กœ๊น… (Monitoring & Logging): ๋ชจ๋“  ํŠธ๋ž˜ํ”ฝ์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ , ์š”์ฒญ ๋ฐ ์‘๋‹ต์— ๋Œ€ํ•œ ๋กœ๊น…์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ์ถ”์ ํ•˜๊ฑฐ๋‚˜ ์„ฑ๋Šฅ์„ ๋ถ„์„ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๐Ÿ“Œ 2. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฐ ์˜์กด์„ฑ ์ถ”๊ฐ€ 

Spring Cloud Gateway๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ตฌํ˜„ํ•œ๋‹ค.

 

โœ… 2-1. Spring Cloud Gateway๋ž€? 

Spring Cloud Gateway๋Š” Spring Boot ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ API Gateway ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ฃผ๋กœ ์™ธ๋ถ€ API ์š”์ฒญ์„ ๋‚ด๋ถ€ ์„œ๋น„์Šค๋กœ ๋ผ์šฐํŒ…ํ•˜๋Š” ์—ญํ• 

  • ์—ฌ๋Ÿฌ ์„œ๋น„์Šค ๊ฐ„์˜ ๋ผ์šฐํŒ…, ํ•„ํ„ฐ๋ง, ์ธ์ฆ/์ธ๊ฐ€ ๋“ฑ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์—ญํ• ๋กœ ๋งŽ์€ ๋™์‹œ์„ฑ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.
  • โญ WebFlux ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•˜๋ฏ€๋กœ, WebMVC์™€ ์ฐจ์ด๋ฅผ ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค.

 

โœ… 2-2. WebFlux๋ž€? 

WebFlux๋Š” ๋น„๋™๊ธฐ ๋ฐ ๋…ผ๋ธ”๋กœํ‚น ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ์Šคํƒ€ํ„ฐ

  • Spring Cloud Gateway๋Š” WebFlux๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.
  • Spring Cloud Gateway๊ฐ€ ๋ฆฌ์•กํ‹ฐ๋ธŒ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ(Mono, Flux ๋“ฑ)์„ ํ™œ์šฉํ•˜๋Š” ๊ตฌ์กฐ

 

๐Ÿ”ฅ spring-boot-starter-webflux vs spring-boot-starter-web

  • spring-boot-starter-webflux: ๋น„๋™๊ธฐ ๋ฐ ๋…ผ๋ธ”๋กœํ‚น ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ์Šคํƒ€ํ„ฐ
  • spring-boot-starter-web: ๋™๊ธฐ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ์Šคํƒ€ํ„ฐ

 

โœ… 2-3. ์˜์กด์„ฑ ์ถ”๊ฐ€ 

๐Ÿ“– build.gradle

// implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway' // Spring Cloud

dependencyManagement {
    imports {
        // Spring Cloud ๊ด€๋ จ ์˜์กด์„ฑ๋“ค์˜ ๋ฒ„์ „ ๊ด€๋ฆฌ
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:2024.0.0"
    }
}

 

๐Ÿ“Œ 3. Eureka์™€ ํ†ตํ•ฉ 

์œ ๋ ˆ์นด ํด๋ผ์ด์–ธํŠธ ์„ค์ • ์ฐธ๊ณ ! 

๐Ÿ”— Eureka

 

โœ… 3-1. ์‚ฌ์šฉ ๋ฐฉ๋ฒ•

  • Eureka ๋ฏธ์‚ฌ์šฉ
    spring.cloud.gateway.routes[0].uri=http://localhost:8081,http://localhost:8082
  • Eureka ์‚ฌ์šฉ
    spring.cloud.gateway.routes[0].uri=lb://SERVICE-1

โš ๏ธ ์ฐธ๊ณ 

  • SERVICE-1์€ spring.application.name=service-1 ์„ค์ •๋œ ์ด๋ฆ„์ด๋‹ค.
  • Eureka์— ๋“ฑ๋กํ•˜๋ ค๋ฉด application name์— ‘_’๋ฅผ ํฌํ•จํ•˜๋ฉด ์•ˆ ๋œ๋‹ค!! (‘-’ ์‚ฌ์šฉ)

 

๐Ÿ“Œ 4. Configuration 

โœ… 4-1. application.properties์—์„œ ์„ค์ •

๐Ÿ“– application.properties

# ๋ผ์šฐํŒ… ID (์ด ์„ค์ •์€ ์ด ๋ผ์šฐํŒ… ๊ทœ์น™์˜ ๊ณ ์œ  ์‹๋ณ„์ž)
spring.cloud.gateway.routes[0].id=to_service1

# ์š”์ฒญ์„ ๋ผ์šฐํŒ…ํ•  URI (๋กœ๋“œ ๋ฐธ๋Ÿฐ์„œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ SERVICE-1์œผ๋กœ ๋ผ์šฐํŒ…)
spring.cloud.gateway.routes[0].uri=lb://SERVICE-1

# ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๊ฒฝ๋กœ (์˜ˆ: `/service1/**`์— ๋งž๋Š” ์š”์ฒญ์„ ํ•ด๋‹น URI๋กœ ๋ผ์šฐํŒ…)
spring.cloud.gateway.routes[0].predicates[0]=Path=/service1/**

# ์š”์ฒญ ํ—ค๋”์— 'X-Gateway-Header'๋ผ๋Š” ํ‚ค์™€ 'Gateway-Value'๋ผ๋Š” ๊ฐ’์„ ์ถ”๊ฐ€
dpring.cloud.gateway.routes[0].filters[0]=AddRequestHeader=X-Gateway-Header, Gateway-Value

 

๐Ÿ“– application.yml

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        - id: service1
          uri: lb://SERVICE-1
          predicates:
            - Path=/service1/**
          filters:
            - StripPrefix=1

 

๐Ÿš€ ์ •๋ฆฌ

  • Gateway๋Š” MSA์—์„œ ์ค‘์š”ํ•œ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, Spring Cloud Gateway๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Eureka๋ฅผ ํ†ตํ•ด ๋™์  ๋ผ์šฐํŒ…์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • application.properties ๋˜๋Š” application.yml์„ ํ™œ์šฉํ•˜์—ฌ ์œ ์—ฐํ•˜๊ฒŒ ์„ค์ • ๊ฐ€๋Šฅํ•˜๋‹ค.

 

โœ… 4-2. GatewayConfig ์ž‘์„ฑ

  • Application์„ค์ • ํŒŒ์ผ์ด Configuration๋กœ ์„ค์ •ํ•  ์ˆ˜ ๋„ ์žˆ๋‹ค.
  • ์ถ”๊ฐ€ ๋กœ์ง์ด ํ•„์š”ํ•  ๋•Œ๋‚˜ ๋””๋ฒ„๊น…์„ ์šฉ์ดํ•˜๊ฒŒ ํ•˜๊ธฐ์œ„ํ•ด ์‚ฌ์šฉ.

๐Ÿ“– GatewayConfig.java: ๊ธฐ๋ณธ ํ˜•ํƒœ

import org.springframework.cloud.gateway.config.EnableGateway;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayConfig {
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                // ์ƒํ’ˆ ์„œ๋น„์Šค
                .route("product_route", r -> r.path("/product/api/products/all")
                        .filters(f -> f.stripPrefix(1))
                        .uri("lb://product-service"))
                // ์ƒํ’ˆ ์„œ๋น„์Šค - ๊ถŒํ•œ ํ•„์š”
                .route("product_route_auth_user", r -> r.path("/product/**")
                        .filters(f -> f.stripPrefix(1))
                        .filter((exchange, chain) -> { // โญ ์กฐ๊ฑด์— ๋”ฐ๋ผ ์š”์ฒญ ์ฐจ๋‹จ ๊ฐ€๋Šฅ
                            List<String> roles = exchange.getAttribute("roles");
                             if (roles != null && roles.contains("ADMIN")) {
                                 return chain.filter(exchange); // ์š”์ฒญ์„ ๋‹ค์Œ ํ•„ํ„ฐ๋กœ ์ „๋‹ฌ
                            } else {                     
                                 exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
                                 return exchange.getResponse().setComplete();
                             }
            		})
                        .uri("lb://product-service"))

                .build();
    }
}

 

โญ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ Filter๋ฅผ ์ ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด?

๐Ÿ”ฅ GatewayFilter ์ข…๋ฅ˜

ํŠน์„ฑ GatewayFilter GatewayFilterFactory
์„ค์ • ๋ฐฉ์‹ ์ƒ์„ฑ์ž ์ฃผ์ž…์œผ๋กœ ํ•„ํ„ฐ์˜ ๋™์ž‘์„ ์„ค์ • Config ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ๋™์ ์œผ๋กœ ์„ค์ • ๊ฐ€๋Šฅ
๋™์  ํŒŒ๋ผ๋ฏธํ„ฐ ์ƒ์„ฑ์ž์—์„œ ์ •์  ๊ฐ’์œผ๋กœ ์„ค์ • apply ๋ฉ”์„œ๋“œ์—์„œ ๋™์ ์œผ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ •
์œ ์—ฐ์„ฑ ์ •์ ์ธ ๊ฐ’์— ์˜์กด ๋‹ค์–‘ํ•œ ์„ค์ •๊ฐ’์„ ๋™์ ์œผ๋กœ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
์ฃผ ์šฉ๋„ ๊ณ ์ •๋œ ๋™์ž‘์„ ํ•˜๋Š” ํ•„ํ„ฐ ๊ตฌํ˜„ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ณ  ๋™์ ์œผ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•„ํ„ฐ

โญ ์•„๋ž˜ ์˜ˆ์‹œ๋Š” jwt ๊ฒ€์ฆ ํ•„ํ„ฐ์ธ๋ฐ Spring Security์—์„œ ๊ฒ€์ฆ ํ•  ๊ฒฝ์šฐ gateway filter์—์„œ๋Š” ๊ฒ€์ฆํ•  ํ•„์š” ์—†๋‹ค!!

 

๐Ÿ“– GatewayConfig.java: GatewayFilter ํ•„ํ„ฐ๋ฅผ ์ ์šฉ

import com.example.gateway.filter.JwtGatewayFilter;

@Configuration
public class GatewayConfig {
    private final JwtGatewayFilter jwtGatewayFilter;
    public GatewayConfig(JwtGatewayFilter jwtGatewayFilter) {
        this.jwtGatewayFilter = jwtGatewayFilter;
    }
    
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                // ์ƒํ’ˆ ์กฐํšŒ ์„œ๋น„์Šค (์ธ์ฆ ์ƒ๋žต)
                .route("product_route", r -> r.path("/product/api/products/all")
                        .filters(f -> f.stripPrefix(1))
                        .uri("lb://product-service"))
                // ์ƒํ’ˆ ์„œ๋น„์Šค (์ธ์ฆ ํ•„์š”)
                .route("product_service", r -> r.path("/product/**")
                        .filters(f -> f.stripPrefix(1)
                                      .filter(jwtGatewayFilter))
                        .uri("lb://product-service"))
                .build();
    }
}

 

๐Ÿ“– JwtGatewayFilter.java: GatewayFilter

import java.util.List;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import com.example.gateway.util.JwtTokenProvider;

import reactor.core.publisher.Mono;

@Component
public class JwtGatewayFilter implements GatewayFilter {

    private final JwtTokenProvider jwtTokenProvider;

    public JwtGatewayFilter(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }
   
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = extractTokenFromRequest(exchange);

        if (token != null && jwtTokenProvider.validateToken(token)) {
            String username = jwtTokenProvider.getUsernameFromToken(token);
            List<String> roles = jwtTokenProvider.getRolesFromToken(token);
           
        // ๊ถŒํ•œ ์ฒดํฌ
        // if (requiredRole  != null && !roles.contains(requiredRole)) {
        //     //log.info("๊ถŒํ•œ ๋ถ€์กฑ: ํ•„์š”ํ•œ ์—ญํ•  = {}, ์‚ฌ์šฉ์ž์˜ ์—ญํ•  = {}", requiredRole, roles);
        //     exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
        //     return exchange.getResponse().setComplete();
        // }
       
        /* ์‚ฌ์šฉ์ž ์ •๋ณด ์ €์žฅ */

        // 1.  ์†์„ฑ ๋งต์— ์ถ”๊ฐ€: gateway ๋‚ด ์š”์ฒญ-์‘๋‹ต ํ๋ฆ„ ๋‚ด์˜ ๋กœ์ง์—์„œ๋งŒ ์‚ฌ์šฉ
        // exchange.getAttributes().put("username", username);
        // exchange.getAttributes().put("roles", roles);

        // 2. ํ—ค๋” ์ถ”๊ฐ€: ํ•˜์œ„ ์„œ๋น„์Šค๋‚˜ ์™ธ๋ถ€ API์—๋„ ๋ฐ˜์˜
        ServerHttpRequest modifiedRequest = exchange.getRequest().mutate()
                .header("X-User-Id", username)
                .header("X-User-Roles", String.join(", ", roles))
                .build();

        exchange = exchange.mutate().request(modifiedRequest).build();

        // 3. SecurityContext์— ์„ค์ •: gateWay์ชฝ springSecurity๋งŒ ์ ์šฉ๋˜๋Š”๊ฑฐ๋ผ ์‚ฌ์šฉ์ž ์„œ๋น„์Šค๋ž‘ ์—ฐ๋™x
        // List<GrantedAuthority> authorities = roles.stream()
        //         .map(role -> new SimpleGrantedAuthority("ROLE_" + role))  // ROLE_ prefix ์ถ”๊ฐ€
        //         .collect(Collectors.toList());
       
        // Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, authorities);
        // SecurityContextHolder.getContext().setAuthentication(authentication);

        } else {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }

        return chain.filter(exchange);
    }

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



๐Ÿ“– GatewayConfig.java: GatewayFilterFactory ํ•„ํ„ฐ๋ฅผ ์ ์šฉ

import com.example.gateway.filter.JwtGatewayFilterFactory;

@Configuration
public class GatewayConfig {

    private JwtGatewayFilterFactory jwtGatewayFilterFactory;

    public GatewayConfig(JwtGatewayFilterFactory jwtGatewayFilterFactory) {
        this.jwtGatewayFilterFactory = jwtGatewayFilterFactory;
    }

     // ๊ณตํ†ต ๋ฉ”์„œ๋“œ: ์—ญํ• ์„ ์„ค์ •ํ•˜๋Š” ๋ฉ”์„œ๋“œ
     private JwtGatewayFilterFactory.Config createConfigWithRole(String role) {
        JwtGatewayFilterFactory.Config config = new JwtGatewayFilterFactory.Config();
        config.setRequiredRole(role);
        return config;
    }

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                // ์ƒํ’ˆ ์„œ๋น„์Šค
                .route("product_route", r -> r.path("/product/api/products/all")
                        .filters(f -> f.stripPrefix(1))
                        .uri("lb://product-service"))
                .route("product_route_auth_user", r -> r.path("/product/**")
                        .filters(f -> f.stripPrefix(1)
                                        .filter(jwtGatewayFilterFactory.apply(createConfigWithRole("USER")))
                                )
                        .uri("lb://product-service")) 
                .build();
    }
}

 

๐Ÿ“– JwtGatewayFilterFactory.java: GatewayFilterFactory

import java.util.List;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import com.example.gateway.util.JwtTokenProvider;

@Component
public class JwtGatewayFilterFactory extends AbstractGatewayFilterFactory<JwtGatewayFilterFactory.Config> {

    private final JwtTokenProvider jwtTokenProvider;

    public JwtGatewayFilterFactory(JwtTokenProvider jwtTokenProvider) {
        super(Config.class);
        this.jwtTokenProvider = jwtTokenProvider;
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String token = extractTokenFromRequest(exchange);

            if (token != null && jwtTokenProvider.validateToken(token)) {
                String username = jwtTokenProvider.getUsernameFromToken(token);
                List<String> roles = jwtTokenProvider.getRolesFromToken(token);

                // ๊ถŒํ•œ ์ฒดํฌ
                if (config.getRequiredRole() != null && !roles.contains(config.getRequiredRole())) {
                    exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN); //403
                    return exchange.getResponse().setComplete();
                }
           
                // ํ—ค๋” ์ถ”๊ฐ€: ํ•˜์œ„ ์„œ๋น„์Šค๋‚˜ ์™ธ๋ถ€ API์—๋„ ๋ฐ˜์˜
                ServerHttpRequest modifiedRequest = exchange.getRequest().mutate()
                        .header("X-User-Name", username)
                        .header("X-User-Roles", String.join(", ", roles))
                        .build();

                exchange = exchange.mutate().request(modifiedRequest).build();
            } else {
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); //401
                return exchange.getResponse().setComplete();
            }

            return chain.filter(exchange);
        };
    }

    public static class Config {
        private String requiredRole;

        public String getRequiredRole() {
            return requiredRole;
        }

        public void setRequiredRole(String requiredRole) {
            this.requiredRole = requiredRole;
        }
    }

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

}

 

 

'Backend > spring cloud (MSA)' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „์†ก  (0) 2025.02.23
[Kafka] ๊ณ ๊ธ‰ ์„ค์ •  (0) 2025.02.23
Kafka๋ž€?  (0) 2025.02.23
Eureka๋ž€?  (0) 2025.02.16
Spring Cloud๋ž€?  (0) 2025.02.16