MSA 아키텍처를 기반으로 프로젝트를 진행하다보니,
고려해야할 사항이 한두가지가 아니다.

그 중에서도 먼저 가장 먼저 짚고 넘어가야할, 인증/인가 방식에 대해서 이야기해보려고한다.
이번 프로젝트에서 우리 팀은, spring cloud를 사용하여 MSA를 구성하려고 한다.
그중에서도 Eureka Discovery Server를 사용하여 어플리케이션 discovery 서버를 구축하고
Spring cloud gateway를 통해서 요청 라우팅 처리를 해줄 예정이다.
그리고 우리 팀이 선택한 인증/인가 방식과, 다른 팀들이 선택한 인증/인가 방식을 공유해보려고 한다
우리 팀이 선택한 인증/인가 방식
MSA 에서 인증/인가 로직을 어떻게 처리해줄까에 대한 고민을 해보았습니다.
저희는 아래 그림과 같이, 인증/인가 로직을 처리해주려고 합니다. (간단하게 그려보았습니다)
클라이언트→gateway→유저서비스 를 통해서 회원가입/로그인이 진행됩니다.
로그인이 성공적으로 진행이 된다면, 유저서비스는 토큰값을 클라이언트에게 반환해줍니다.
그럼 클라이언트는 토큰값을 갖고 gateway로 요청을 보냅니다.
요청을 보내면 gateway 자체에서 토큰에 대한 인증/인가를 진행합니다.
인증/인가로직이 성공한다면,
gateway는 토큰에 포함된 username과, role값을 requestHeader에 담아서 각 서비스에 라우팅 해줍니다.
그러면 각 서비스는 해당 값을 사용하여 유저에 대한 로직을 처리하면 됩니다.
서비스 간의 통신은, FeignClient를 사용하기로 하였는데,
FeignClient로 통신하면서 토큰의 인증/인가 작업은 필요하지 않을까요? 라는 궁금증이 생길 수 있는데,
모든 요청은 게이트웨이를 거쳐서 들어오고, 인증/인가 작업은 이미 끝났기 때문에 서비스간의 통신은,
굳이 인증/인가 작업이 필요하지 않을 것이라고 판단하였습니다.
만약, FeignClient 통신에서 인증/인가 작업이 필요하다고 생각하여,
다시 게이트웨이를 거쳐서 인증/인가 작업을 처리하게 된다면,
비용이 많이 들것이라고 생각합니다.
추가적으로, 실무에서는 서비스 내부용 gateway를 따로 두어서
인증/인가 작업을 처리하는 곳도 존재 한다라는 점도 새롭게 알게되었습니다.
하지만 서비스 별 통신에서, 인증/인가 작업이 반드시 필요한 경우에만 내부용 게이트웨이를 두는 것이 맞다고 판단하였고,
저희 서비스는 주문과 배송, 배송과 배송담당자 등의 통신에서 인증/인가 작업은 필요 없다고 판단하여,
서버 내부용 게이트웨이를 두지 않는 방식으로 결정하였습니다.
다른 팀들이 선택한 인증/인가 방식
Netflix의 passport 전략으로 개발을 진행할 예정입니다!
각 서버간 통신에서 인증/인가를 거치게되면 서버 자원을 너무 소모한다는 생각이 들어서,
게이트웨이에서 일괄적인 인증/인가를 처리하게 되었습니다.
이 방식으로 서비스간 인증/인가 요청 감소, 유저 상태를 헤더에 유지하면서 불필요한 유저 API 호출 최소화,
JWT 기반보다 유저 정보 갱신이 빠르게 이루어지는 장점이 있다 생각했습니다.
단점으로는 유저 정보 변경 실시간 반영이 어려울 수 있다는 점입니다.
API Gateway에서 유저 정보를 캐싱해서 사용해야 하기 때문에 유저 정보가 변경되었을때 정합성이 깨질 수 있습니다.
또한 유저의 권한이 변경되거나 계정이 정지되었을 때 기존 passport가 계속 사용되면 잘못된 권한이 유지될 수 있습니다.
또한 대량의 passport를 저장하고 조회하는 과정이 발생되면 모든 요청을 관리하는 API gateway에서 부하가 발생할 수 있으므로 Redis의 분산 캐시 시스템 또는 Ribbon을 활용한 로드 밸런싱이 필요해 질 것으로 보입니다.
이 외에도 고려하고 있는 첫 번째 방법은 Token Exchange 방식입니다.
클라이언트가 JWT토큰을 API Gateway에 전달하면,
Gateway가 내부에서 사용 가능한 Short-lived Access Token을 생성하여
내부에서 사용 가능한 토큰으로 안전하게 통신을 하는 방법입니다.
장점으로는 Short-lived Token을 사용하여 토큰 유효 시간은 제한하고, 보안을 강화할 수 있는 장점이 있지만,
내부 서비스에서 Token Exchange를 추가적으로 개발해야 하는 단점과
인증/인가를 2번에 걸쳐 진행해야 한다는 부분이 있을 것 같습니다.
두 번째 방법은 말씀하신 것처럼 내부에 사설 API Gateway를 두어서 내부에서 사용할 수 있는 private한 gateway로만 통신을 하는 방법인데, 저희가 아직 MSA환경에 익숙하지 않아 이후에 시간이 된다면 추가로 고도화해볼 수 있는 방법이라 후순위로 두고 있습니다.
그 외에도 클라이언트와 게이트웨이간 상호 인증(mTLS) 암호화로 패킷 분석을 더 복잡하게 만들어 보안을 강화하는 방식도 실제 기업에서 사용하는 걸로 알고 있는데 종단간 암호화에 대한 개념은 아직 부족한지라 조금 더 공부 후에 고려해볼 것 같습니다.
다른 방식
결론부터 말씀드리면, 인증 및 인가는 다음과 같이 처리할 예정입니다.
로그인, 회원가입: 게이트웨이 -> 유저서비스
토큰을 통한 인증인가: 게이트웨이에서 진행
서비스간 통신: 게이트웨이에서 받아온 헤더 정보를 유지하여 처리
각 서비스에서 개별적으로 인증/인가를 수행할지 고민했지만,
이 경우 모든 서비스가 유저 서비스와 직접 통신해야 하므로 비효율적이라고 판단했습니다.
또한, 각 서비스의 IP 주소 유출 가능성에 대한 우려는 네트워크 단에서의 프라이빗 처리와
서브넷 마스크 등의 보안 조치를 통해 작업이 이루어지고,
이를 바탕으로 모든 요청이 반드시 게이트웨이를 거치도록 보안이 적용된 상태라는 전제하에 진행할 예정입니다.
또 다른 방식
인증
- 로그인
- API Gateway의 SpringSecurity Filter에서 처리
- 액세스 토큰과 리프레쉬 토큰 발급
- Redis에 (리프레쉬 토큰 + 디바이스 정보 + IP ) 저장
- 로그아웃 (유저 블랙리스트화)
- Redis에 저장된 인증 정보 삭제
인가
- 액세스 토큰 유효
- API Gateway에서 Passport 발급 후 헤더에 실어 하위 서비스에서 인가 없이 서비스 로직 실행하도록 전달
- 액세스 토큰 만료 && 리프레쉬 토큰 유효
- 액세스 토큰 재발급
- 리프레쉬 토큰 재발급 (로테이션)
- Redis에 (리프레쉬 토큰 + 디바이스 정보 + IP) 저장 (갱신)
- API Gateway에서 Passport 발급 후 헤더에 실어 하위 서비스에서 인가 없이 서비스 로직 실행하도록 전달
- 액세스 토큰 만료 && 리프레쉬 토큰 무효
- 로그인 유도
해킹
- 탈취된 리프레쉬 토큰이 사용될 경우
- Redis에 사용자 디바이스 정보와 IP 정보를 비교해 검증 가능
- 이 정보까지 탈취되었다면 이메일이나 SMS 인증 요구 방식 고려 가능
Q. 혹시 IP, 디바이스 정보는 어떻게 수집은 어떻게 하시는지 여쭤봐도 될까요? IP수집이 네트워크 환경적인 부분에서 제약이 많은 걸로 알고있어서요!
A. IP 수집은 말씀대로 로드밸런서나 프록시를 지나치면서 클라이언트의 IP를 얻기 힘든 경우가 있는 것으로 알고 있습니다!
그래서 X-Forwarded-For 헤더를 사용하거나 Proxy Client IP를 얻어오는 헤더를 사용해 사용자의 IP를 가져오려고 합니다.
그 방법까지 잘 되지 않는다면 그때 차선책을 찾아보려고 하고 있습니다.
또, 디바이스 정보는 프론트엔드에서 UserAgent 정보를 요청하거나
디바이스 지문 정보를 요청해 클라이언트 정보를 구분하려고 했습니다.
'Back-End > JavaSpring' 카테고리의 다른 글
Bean과 Spring IoC 컨테이너 (0) | 2025.03.31 |
---|---|
JDBC가 등장한 이유와 JPA로 넘어간 이유 (3) | 2025.03.20 |
DDD 기반 프로젝트 패키지 구조 설계 (SpringBoot) (0) | 2025.03.12 |
DIP를 통해 반가운(?) 순환참조 해결하기 (springboot) (0) | 2025.03.10 |
요즘은 클라이언트에게 정적 페이지/동적 페이지가 아닌, 데이터를 반환해준다고? (Spring Boot) (0) | 2025.02.27 |