Mingyum-Kim / Iamhere

😉 국내 거주 외국인 매칭 서비스 "I-am-here" 😉
1 stars 0 forks source link

Spring Gateway에서 JWT 인증 구현하기 #33

Open Mingyum-Kim opened 1 year ago

Mingyum-Kim commented 1 year ago

🖤 설명

⭐체크리스트

❗ 주의 사항

참고 : https://ch4njun.tistory.com/237 위 블로그를 보면, API Gateway의 Filter에서 토큰 인증과정을 수행하면 토큰에 저장되어있는 UserContext 정보를 꺼낼 수 없다고 나와있다. 토큰을 발급 받는 주체도 결국 현재 사용자이고, 발급 시 Claims에 User Mail정보를 넣어서 반환할 것이다. 나는 기존에 Authentication을 이용해 아래와 같이 사용자의 정보를 꺼내왔었는데, 이러한 방법을 사용할 수 없는 건지 확인할 필요가 있다.

    @GetMapping("/get-current-member")
    public Long getCurrentMember(Authentication authentication){
        log.info("authentication.getName() : " + authentication.getName());
        Member member = memberService.getMember(authentication.getName());
        return member.getId();
    }
Mingyum-Kim commented 1 year ago

API Gateway를 사용하면 마이크로 서비스에서 Authentication 객체를 가져올 수 없는 이유는 Security Context 에 Authentication 객체가 담기는 것은 마이크로서비스가 아닌 Spring Gateway 서버에서 일어나기 때문이다. 마이크로서비스는 JWT 인증 로직이 없을 뿐더러 당연히 어떤 토큰이 사용되었는지 전달받지 않기 때문에 (앞단에 있는 Spring Gateway에서 전부 처리해주고 URI 을 바로 실행해주므로) Spring Gateway에 있는 JWT 를 사용할 수 없다.

그래서, Spring Gateway에서 JWT 유효성 검사를 수행하고 그 결과를 Authorization 헤더로 서비스에 전송하는 방식을 사용한다. 마이크로 서비스에서 이 헤더를 읽고, Claims를 파싱하는 방법으로 사용자의 정보를 추출할 수 있다.

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

    public CustomHeaderFilter() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // JWT Claims 정보를 추출한 후, 요청 헤더에 추가하는 로직
            String jwtClaims = extractJwtClaimsFromGateway(exchange);
            exchange.getRequest().mutate().header("X-JWT-Claims", jwtClaims).build();

            return chain.filter(exchange);
        };
    }

    private String extractJwtClaimsFromGateway(ServerWebExchange exchange) {
        // API Gateway에서 JWT 유효성 검사 등을 수행하여 Claims 정보 추출
        // 실제 구현은 여기에 맞게 처리해야 합니다.
        return "your_jwt_claims";
    }

    public static class Config {
        // 필요에 따라 구성 옵션을 추가할 수 있습니다.
    }
}

이후 마이크로 서비스에서 아래 로직을 추가하면 JWT Claims 정보를 추출할 수 있다.

request.getHeader("X-JWT-Claims")
Mingyum-Kim commented 1 year ago

UserDetailsService를 사용해야하는 것은 운명이구나 ..

참고 : https://imprint.tistory.com/219 로그인에서 썼던 두 가지 로직을 UsernamePasswordAuthenticationFilter를 상속한 AuthenticationFilter에서 구현한다.

(1) 사용자 확인 attempAuthentication에서 구현한다. UserDetails를 사용하여 구현한다면 getAuthenticationManager을 이용해 구현할 수 있다.

(2) Jwt 발급 로직 successfulAuthentication 함수에서 구현한다. 이 부분에서 Claims에 사용자의 정보를 토큰에 주입할 수 있다. 만든 Authentication Filter을 WebSecurity에서 permitAll() 이외의 URI 요청에 대해 거치도록 한다. 이로써 Token을 Gateway에 전달할 수 있게된다.

마지막으로 AuthorizationHeaderFilter을 Gateway에 추가하여, 로그인이 필요한 요청이 들어왔을 때 이 Filter를 거치도록 한다. 참고로, 저 블로그 예시에서는 WebSercurityConfigurerAdaper 을 상속한 이전 방식으로 Spring Security를 구현하고 있다. 이에 따라 autenticationManager()을 사용하지 못해 getAuthenticationFilter함수를 작성하는 것에 무리가 있다. 이럴 경우, Bean 등록으로 직접 구현해야한다.

참고 : https://wonit.tistory.com/500