스프링에는 Reactive 라이브러리와 mvc 라이브러리가 존재하고
이들의 차이점은 비동기 지원 여부에 따라 나뉨.
Reactive Gateway 의존성을 추가하면 Reactive web 의존성이 추가로 설치됨

Gateway(mvc) 의존성을 추가하면 web 의존성이 추가로 설치됨.
둘의 차이점은 동기 방식(tomcat)과 비동기 방식(netty)의 처리.
다른 서비스들과 마찬가지로 Eureka 서버에 등록하기 위해 Eureka Client 의존성을 추가하여 설정해준다.
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka
spring:
cloud:
gateway:
routes:
- id: user-service # 로그인
uri: lb://USER-SERVICE
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/users
- 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}
- AuthorizationFilter
- id: catalog-service # 카탈로그 서비스
uri: lb://CATALOG-SERVICE
predicates:
- Path=/catalog-service/**
filters:
- RewritePath=/catalog-service/(?<segment>.*), /$\{segment}
- id: order-service # 주문 서비스
uri: lb://ORDER-SERVICE
predicates:
- Path=/order-service/**
filters:
- RewritePath=/order-service/(?<segment>.*), /$\{segment}
@Component
@Slf4j
public class AuthorizationFilter extends AbstractGatewayFilterFactory<AuthorizationFilter.Config> {
Environment env;
public AuthorizationFilter(Environment env){
super(Config.class);
this.env=env;
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
if(!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)){
return onError(exchange, "No authorization header", HttpStatus.UNAUTHORIZED);
}
String authorizationHeader = request.getHeaders().get(org.springframework.http.HttpHeaders.AUTHORIZATION).get(0);
String jwt = authorizationHeader.replace("Bearer ","");
if(!isJwtValid(jwt)){
return onError(exchange, "Jwt token is not valid", HttpStatus.UNAUTHORIZED);
}
return chain.filter(exchange);
};
}
private boolean isJwtValid(String jwt) {
boolean returnValue = true;
byte[] secretKeyBytes = Base64.getEncoder().encode(env.getProperty("token.secret").getBytes());
SecretKey secretKey = Keys.hmacShaKeyFor(secretKeyBytes);
String subject=null;
JwtParser parser = Jwts.parser().verifyWith(secretKey).build();
try{
subject = parser.parseSignedClaims(jwt).getPayload().getSubject();
}catch (Exception ex){
returnValue = false;
}
if(subject==null||subject.isEmpty()){
returnValue = false;
}
return returnValue;
}
// Mono,Flux 란? Webflux에서 데이터를 처리하는 단위 단일 이냐 다중이냐에 따라서 구분해서 사용됨
private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
ServerHttpResponse response= exchange.getResponse();
response.setStatusCode(httpStatus);
log.error(err);
return response.setComplete();
}
public static class Config{
}
}
exchange.getRequest 에서 현재 요청을 가져와서Unauthorized 401 상태코드를 반환한다.Bearer 를 제거한 후, isJwtValid 메서드를 실행한다.Unauthorized 401 상태코드를 반환한다.여기서 AbstractGatewayFilterFactory<C> 를 상속받아 사용자 정의 필터를 만들었다. 제너릭타입으로는 커스터 필터인 AuthorizationFilter 내부에 정적 클래스 Config 를 할당해주었다.
제너릭 타입으로 들어가는 Config클래스는 필터의 설정을 추가하기 위한 용도로 사용되지만, 설정 없이도 구현 가능하여 클래스 정의만 해두었다.
AuthorizationFilter에 @Component 어노테이션을 사용해 스프링 빈으로 등록해두었다.
따라서 위의 Yaml 설정에서 AuthorizationFilter 이름만으로 원하는 로직에 필터를 적용시킬 수 있다.

github 참고)
https://github.com/ajou20658/spring-cloud-toy/tree/master/gateway