인증과 권한 [4] gateway filter 설정

최준호·2022년 3월 6일
0

Microservice Architecture

목록 보기
18/32
post-thumbnail

user service에서 jwt를 통해 토큰을 발급받는 기능을 추가했으니 gateway에서 filter 기능을 통해 로그인 인증 처리를 해보자.

🔨jwt 설정

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.1</version>
</dependency>

jwt를 추가해준다.

javax.xml.bind의 경우 아래 예제를 진행하다보면 getBody() 메서드를 실행했을 때 해당 라이브러리가 비어 있어서 오류가 발생하기 때문에 따로 라이브러리를 추가했다.

@Component
@Slf4j
public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory<AuthorizationHeaderFilter.Config> {
    private Environment env;

    //생성시 Config class를 상속받은 Factory로 넘겨줘야해서 lombok을 사용하지 않고 다음과 같이 처리
    public AuthorizationHeaderFilter(Environment env) {
        super(Config.class);
        this.env = env;
    }

    public static class Config{

    }

    @Override
    public GatewayFilter apply(Config config) {
        return ((exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();

            //header에 HttpHeaders.AUTHORIZATION 값이 존재하는지 확인
            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);
        });
    }

    //token이 유효한지 확인
    private boolean isJwtValid(String jwt) {
        boolean returnValue = true;

        String subject = null;
        try {
            subject = Jwts.parser().setSigningKey(env.getProperty("token.secret"))  //secret key 값을 통해 parse
                    .parseClaimsJws(jwt).getBody()  //token의 내용을 가져옴
                    .getSubject();
        }catch (Exception e){
            returnValue = false;
        }

        if(subject == null || subject.equals("")){
            returnValue = false;
        }
        return returnValue;
    }

    //에러 발생시 에러 값을 response
    //Mono, Flux -> Spring WebFlux 개념 / 데이터 단위 단일=Mono, 복수=Flux
    private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(httpStatus);

        log.error(err);
        return response.setComplete();  //Mono 데이터 return
    }
}

AuthorizationHeaderFilter 파일을 만들어서 내용을 다음과 같이 입력한다. 여기서 token secret 값은 yml에서 설정하지 않았기 때문에 yml의 설정에도 추가해줘야한다.

...

spring:
  application:  #gateway service 이름
    name: apigateway-service
  cloud:
    gateway:  #gateway 설정
      default-filters:
        - name : GlobalFilter #Global Filter로 지정된 java 파일 이름
          args:
            baseMessage: Spring Cloud Gateway Global Filter
            preLogger: true
            postLogger: true
      routes:
#        ...
        - id: user-service
          uri: lb://USER-SERVICE
          predicates:
            - Path=/user-service/**
            - Method=GET
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/user-service(?<segment>.*), /$\{segment}
            - AuthorizationHeaderFilter
        ...

token:
  expiration_time: 86400000
  secret: user_token

yml의 중요 내용만 설정해준다면 위의 내용과 같고 user-service에서 AuthorizationHeaderFilter filter를 추가해주자.

👊실제 테스트해보기

로그인하여 발급 받은 토큰이 없을 때 health_check를 실행시켜보자

이제 GET user-service/** 에서는 token 없이 접근할 경우 401 에러가 떠버린다.

발급 받은 토큰을 사용하여 다시 요청해보자.

발급 받은 토큰 정보를 입력한 뒤 요청하면 다음과 같이 정상적으로 처리됨을 확인할 수 있다. 그럼 이 정보가 어떻게 진행되는지 로직을 살펴보자

우리가 작성한 AuthorizationHeaderFilterapply()에 요청이 들어오고

우리가 요청한 Header에 Authorization : value 가 존재하는지 확인을 먼저 해준다.
우리는 Bearer type으로 요청했기 때문에 value에 Bearer + token으로 값이 넘어오는데 해당 값을 replace()로 처리해주고 isJwtValid()를 통해 유효성을 체크한다.

넘어온 값은 Jwt를 통해 parse해주고 해당 subject를 확인하면 81cae99b-0402-4076-9c57-585a145033eb 값이 들어있는데 이 값은 우리가 token을 만들때 사용했던 userId값이다.
정말인지 확인을 위해

login 했을 때 반환된 값을 보면 우리가 반환 받은 값과 동일한 값을 확인할 수 있다.

정상적으로 token 정보를 넘긴 반환 값을 받을 수 있었다.

profile
코딩을 깔끔하게 하고 싶어하는 초보 개발자 (편하게 글을 쓰기위해 반말체를 사용하고 있습니다! 양해 부탁드려요!) 현재 KakaoVX 근무중입니다!

0개의 댓글