Login Service[3] gateway 인증 추가

최준호·2022년 3월 21일
0

jayeon

목록 보기
9/10
post-thumbnail

지난 포스트까지 login 토큰을 발급해봤다. 이젠 로그인 이후 다른 서비스를 사용할 때 토큰 값이 유효한지 체크후에 진행할 수 있도록 gateway에서 처리해보자.

🔨임의의 Service 생성

login-servie에서는 자체적으로 filter로 거르고 있기 때문에 gateway에서 정확히 걸러지는지 확인하기 위해 어차피 order-service를 생성해야되기 때문에 임의로 하나의 서비스를 생성해서 추가해놔보자.

👉Service 생성

선택한 옵션들은 다음과 같다.

server:
  port: 0

spring:
  application:
    name: order-service
    
eureka:
  instance:
    instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}

설정 정보를 입력해주고

아직 config server에 등록하지 않고 사용하려면 gradle에서 config는 주석처리하고 사용하자


@SpringBootApplication
@EnableDiscoveryClient
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

}

Eureka에서 찾을수 있도록 설정해준다.

@RestController
@RequestMapping("/")
public class HelloController {
    @GetMapping("/hello")
    public String hello(){
        return "hello Order Service!";
    }
}

이제 요청을 확인할 수 있도록 설정을 해주고

👉Gateway 설정 변경

server:
  port: 8000

eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka
# token 추가해줘야함!!!!
token:
  expiration_time: 86400000 #ms단위
  secret: 토큰 값
  
spring:
  application:  #gateway service 이름름
    name: gateway-service
  cloud:
    gateway:  #gateway 설정
      routes:
        # login service
        - id: login-service
          uri: lb://LOGIN-SERVICE
          predicates:
            - Path=/login-service/**
            - Method=GET, POST
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/login-service(?<segment>.*), /$\{segment}
        # order service
        - id: order-service
          uri: lb://ORDER-SERVICE
          predicates:
            - Path=/order-service/**
            - Method=GET, POST
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/order-service(?<segment>.*), /$\{segment}

gateway.yml파일의 내용을 다음과 같이 설정해준다. order service를 추가해주었다.


그리고나서 테스트를 진행해보면 지금은 토큰 값이 없어도 정상적으로 처리되는 것을 확인할 수 있다. 이제 토큰이 없으면 처리 못하도록 수정해보자.

🔨Gateway 설정 변경

implementation 'io.jsonwebtoken:jjwt:0.9.1'
implementation group: 'javax.xml.bind', name: 'jaxb-api', version: '2.1'

의존성 2개를 추가하고

@Component
@Slf4j
@RequiredArgsConstructor
public class AuthFilter extends AbstractGatewayFilterFactory<AuthFilter.Config> {

    private final Environment env;

    public static class Config{
        //설정에 필요한 내용 정의
    }

    //인증 요청시 확인
    @Override
    public GatewayFilter apply(Config config) {
        return ((exchange, chain)->{
            ServerHttpRequest request = exchange.getRequest();

            if(!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)){
                //헤더에 AUTHORIZATION key 자체가 존재하지 않을 경우
                return onError(exchange, "no AUTHORIZATION header", HttpStatus.UNAUTHORIZED);   //401 반환
            }

            String authorizationHeader = request.getHeaders().get(org.springframework.http.HttpHeaders.AUTHORIZATION).get(0);   //AUTHORIZATION key 값으로 value 가져옴
            String jwt = authorizationHeader.replace("Bearer", ""); //JWT, OAuth는 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
    }
}

filter를 추가하자. filter의 내용은 login-service의 인증과 거의 비슷하지만 조금 다른 부분이라면 WebFlux의 Mon와 ServerHttp를 사용한다는 점이다. 아직 깊게 내용을 봐본적이 없어서 상세히 모르지만 꼭 학습해야할 부분이다!

🔨gateway 설정파일 수정

server:
  port: 8000

eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka

token:
  expiration_time: 86400000 #ms단위
  secret: 토큰 값

spring:
  application:  #gateway service 이름름
    name: gateway-service
  cloud:
    gateway:  #gateway 설정
      routes:
        # login service
        - id: login-service
          uri: lb://LOGIN-SERVICE
          predicates:
            - Path=/login-service/**
            - Method=GET, POST
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/login-service(?<segment>.*), /$\{segment}
        # order service
        - id: order-service
          uri: lb://ORDER-SERVICE
          predicates:
            - Path=/order-service/**
            - Method=GET, POST
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/order-service(?<segment>.*), /$\{segment}
            - AuthFilter	#토큰 처리 필터 적용

최종적으로 gateway의 설정 파일이 다음과 같은 내용으로 변경되어야한다.

👊테스트 해보기

그럼 이제는 401로 반환하면서


gateway에서는 우리가 설정해놓은 에러 메세지를 출력해준다.

회원가입 후

로그인하여 발급받은 토큰을

적용시켜서 정상적으로 반환받는 것을 확인할 수 있었다!

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

0개의 댓글