티스토리 뷰

- 코드를 원하시는 분은 하단에 코드를 첨부하니 확인해주세요. 

JWT 토큰을 생성하는 법을 알아봤으니 이제 해당 토큰을 활용해서 인증을 받는 기능을 구현하고자 합니다. 

마찬가지로 pom.xml에 JJWT의 Dependency를 추가해줍니다. 

#pom.xml

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

jjwt는 Jwt사용에 있어서 필수적인 요소인데 이를 인증하기 위해서는 jaxb-api가 필요합니다. 해당 dependency가 없으면 오류가 발생하는데 이는 올바른 응답을 위해서 반드시 요청에 Converter가 필요하다 정도로 생각하면 될 거 같습니다.

 

다음으로는 AuthorizationHeaderFilter.Class를 생성하고 다음과 같이 입력합니다. 약간의 주석이 달려있으니 참고하시면 좋을 것 같습니다. 

#AuthorizationHeaderFilter.java

package com.example.apigatewayservice.filter;

import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

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

    @Autowired
    public AuthorizationHeaderFilter(Environment env){
        // 부모 클래스의 Config 설정을 그대로 가져오는 것은 반드시 선행되어야 함. 
        super(Config.class);
        this.env = env;
    }

    //추후에 작성할 Config 클래스
    public static class Config{

    }

    // Override

    /*
     * 1. 해당 필터는 Jwt 토큰을 인증하는 과정을 거칩니다.
     * 2. 기본적으로 토큰정보 확인 하는 과정 후 인증완료의 메커니즘 입니다.
     * 3. login -> token -> users (with token) -> header(include token) 안에 token 정보가 있습니다.
     */
    @Override
    public GatewayFilter apply(Config config) {
        return ((exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();


            // 해당 IF문은 인증 실패 시 에러처리 하는 문구입니다.
            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);
            
            // Bearer 방식으로 토큰인증을 진행하기에 토큰 머리쪽에 Bearer가 붙어서 옵니다. 해당 토큰에서 Bearer를 삭제하는 과정입니다.
            String jwt = authorizationHeader.replace("Bearer","");

			// 최종적으로 유효한 토큰인지 체크 합니다. 유효한 토큰이 아니라면 onError를 리턴하고 해당 URL에 접근을 막습니다. 
       
            if(!isJwtValid(jwt)){
                return onError(exchange,"JWT token is not vaild", HttpStatus.UNAUTHORIZED);
            }
            return chain.filter(exchange);
        });
    }

    //JWT 유효성 체크
    private boolean isJwtValid(String jwt) {
        boolean returnValue = true;

        String subject = null;
        try {
            // 토큰의 Parse과정을 통해서 유효성을 체크합니다. 기본적으로 
            // Body 안의 sub라는 항목이 있는지 체크합니다.
            subject = Jwts.parser().setSigningKey(env.getProperty("token.secret"))
                    .parseClaimsJws(jwt).getBody()
                    .getSubject();
        }catch(Exception ex){
            returnValue = false;
        }

        if (subject == null || subject.isEmpty()){
            returnValue = false;
        }
        //returnValue를 리턴합니다 해당 값이 true 냐 false 따라서 접근 여부를 결정할 예정입니다.
        return returnValue;
    }

    // Mono, Flux -> Spring WebFlux
    private Mono<Void> onError(ServerWebExchange exchange, String err, HttpStatus httpStatus) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(httpStatus);

        log.error(err);

        //Mono타입으로 전달할 수 있는 함수
        return response.setComplete();
    }
}

 

마지막으로 application.yml을 수정해줍니다. 

#application.yml 

Login 과 회원등록은 인증이 필요한 Url이 아니기에 그대로 두고, 나머지 조회기능을 가진 기능들은 

인증 후에 접속이 가능하도록 설정을 해줍니다.

            - id: user-service
              uri: lb://USER-SERVICE
              predicates:
                 - Path=/user-service/**
                 - Method=GET
              filters:
                 - RemoveRequestHeader=Cookie
                 - RewritePath=/user-service/(?<segment>.*),/$\{segment}
                 - AuthorizationHeaderFilter

마지막으로 해당 Gateway 에서도 동일한 토큰을 발급해줍니다.

token:
   secret: ggpark_token

여기까지 진행하면 토큰을 인증하는 과정은 끝났습니다.  그럼 해당 토큰을 활용해 인증하는 과정을 POST-MAN으로 

진행하면 끝입니다.

실행할 서비스 목록입니다.

Ecommerce-Service ( Eureka-Server ) 

Gateway-Service 

User-Service

#Test Case ( 회원 등록 ) 

먼저 기존에 만들어둔 User-Service 에서 회원 등록을 활용해서 등록을 진행해줍니다. 

#Test Case ( 로그인  & 토큰 확인 )

그리고 Login을 통해서 발급받은 토큰을 확인합니다. Headers 최상단에 있습니다. 복사해주세요.

#Test Case ( 토큰 인증 )

먼저 토큰값으로 인증해야하는지 테스트를 하기 위해서 No Auth 로 /user-service/welcome 으로 접속해봅시다.

401Error가 출력된다면 정상적으로 인증 시스템이 가동중입니다. 

그리고 저희가 사용할 방식은 Bearer Token 입니다. 해당 값으로 맞춰준 뒤 복사한 토큰값을 붙혀넣고 다시 요청을

Send 합니다. 200OK와 함께 Greeting Message 가 잘 출력된다면 성공입니다. 

감사합니다.

 

 

- 아래에 해당 프로젝트를 첨부합니다. 감사합니다.

apigateway-service.7z
0.06MB

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함