[AWS] api gateway 서버리스 JWT 발급

pjh·2025년 7월 30일

AWS

목록 보기
6/7

개요

JWT 기반 인증 시스템을 직접 서버리스 환경(AWS API Gateway + Lambda + ElastiCache + RDS)에서 구축한 경험을 정리

MSA 프로젝트시 각 서비스별로 JWT를 발급, 인증하는 것보다는 Api gateway를 이용하여 유저부분을 담당하게하여 각 서비스에 JWT를 주어 복잡도 감소시킴

제목이 서버리스인 이유는 딱 jwt 발급까지만 서버리스이기 때문. 그 후에 Spring Boot 연동 및 클라이언트부터 인증 처리, 데이터 저장

스택

  • API Gateway| REST API 엔드포인트 제공, Lambda로 요청 프록시
  • Lambda (Node.js)| 로그인/회원가입 처리, JWT 발급, Redis 저장
  • ElastiCache (Redis)| Refresh Token 저장소 (7일 TTL)
  • RDS (MySQL)| 사용자 정보 저장
  • Spring Boot (Kotlin)| 백엔드 리소스 서버, JWT 기반 인가 처리

❓ JWT 검증 위치

  • Lambda
    • Lambda Authorizer: Api gateway에서 Jwt를 검증하는 Lambda를 붙임
  • API Gateway
    • Cognito Authorizer: API Gateway에서 Cognito로 토큰 검증 위임
  • Spring boot
    • Spring security: 직접 JwtFilter를 설정하여 Jwt 검증

여기서는 검증을 백엔드에 위임했지만, 만약 백엔드 서버가 없다는 가정하에는 위 2개 중 하나가 좋은 방법.

흐름도

api gateway


리소스를 등록하고 스테이지로 배포를 해주면 완료

스테이지에 있는 URL 호출을 복사하여 경로를 붙여주어 직접 api 테스트

Lambda

👨‍💻 로그인 처리

node, python, java 등 여러 언어로 만들기 가능.
복잡한 데이터처리는 python이 가진 데이터 라이브러리를 이용하여 사용하는게 좋겠지만, 간단한 테스트용으로는 node로 생성하는게 좋을 것 같다.

RDS와 ElastiCache를 연결하여 RDS에 있는 사용자정보를 불러와 JWT를 만들고 클라이언트에게 응답, 그리고 Redis에 리프레시 토큰을 저장하여 만료된 액세스 토큰 요청시 Lambda를 이용해 리프레시 토큰을 통해 엑세스 토큰 재발급


📋 소스

  • login.js
    // db connect & get user id

    const userId = ...
    // JWT 발급
    const accessToken = jwt.sign({ sub: userId }, process.env.JWT_SECRET, {
      expiresIn: '15m',
    });
    const refreshToken = jwt.sign({ sub: userId }, process.env.JWT_SECRET, {
      expiresIn: '1d',
    });

    // Redis에 Refresh Token 저장
    await redis.set(`refresh:${userId}`, refreshToken, 'EX', 60 * 60 * 24 * 1);

Spring boot

jwt 인증처리

  • jwt 필터(OncePerRequestFilter)에서 Authorization 헤더의 토큰 추출
  • 서명 검증 후 SecurityContext에 사용자 정보 저장

📋 소스

  • JwtAuthFilter
class JwtAuthFilter(
    private val jwtProvider: JwtProvider // 토큰 검증 클래스
): OncePerRequestFilter() {

    override fun doFilterInternal(
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain
    ) {
        val authHeader = request.getHeader("Authorization")

        if (authHeader?.startsWith("Bearer ") == true) {
            val token = authHeader.removePrefix("Bearer ")
            if(jwtProvider.validateToken(token)) {
                val userId = jwtProvider.getUserId(token)
                // user id 인증 저장
                val auth = UsernamePasswordAuthenticationToken(userId, null, emptyList())
                // 인증 정보 저장
                SecurityContextHolder.getContext().authentication = auth
            }
        }
        filterChain.doFilter(request, response)
    }
}
  • Controller
    @GetMapping("/test")
    fun test(principal: Principal): String =
        "My ID = ${principal.name}"

principal로 클라이언트가 요청한 jwt를 확인 후 저장했던 subject를 이용하여 직접 DB 혹은 필요한 서비스에 연결

마무리

API Gateway + Lambda만으로도 인증 처리하여 간소화 된 서비스 운영이 가능해짐

0개의 댓글