JWT Payload에 있는 사용자 획득방법 (Argument Resolver)

Hansu Park·2023년 10월 1일

BackEnd

목록 보기
3/3

결론(예시)

Controller

public @ResponseBody
ResponseEntity<UserResponse> getUserInfo(@Login User loggedInUser) {
   UserResponse user = userService.getUserInfo(loggedInUser);
   return new ResponseEntity<>(user, HttpStatus.OK);
}

Service

@Override
public UserResponse getUserInfo(User loggedInUser) {
   User userInDB = userMapper.getUserById(loggedInUser.getId().longValue());

   return UserConverter.INSTANCE.toUserResponse(userInDB);
}

클래스 혹은 메서드에 @Auth애너테이션이 붙은 컨트롤러의 메서드에, (JWT 검증이 이루어지는 API 엔드포인트)
@Login User user와 같은 형식을 작성한다면

  • Interceptor-ArgumentResolver에서 알아서 User 객체 주입
  • Swagger 상에는 감춰짐

의 효과를 얻을 수 있다.

도입 배경

기존 로직의 좋지않은 계층간 참조 (Service Layer에서 Presentation Layer의 계층을 참조)

  • Service인 JwtValidator에서
    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest() 을 이용하여 Jwt 토큰을 획득하여 검증함.
  • JwtValidator에서 Header 자체를 이용하여 토큰을 검증함.

=> Service 계층에서 서블릿, 헤더 자체를 호출하지 않도록 수정해야 했음.

처리 방안

  1. Filter에서 검증을 한 후, 파라미터에 검증한 사용자 정보를 담음 => 컨트롤러에서 파라미터를 조회하여 획득
  • 기존 인터셉터 방식의 검증 로직에 부합하지 않음.
  • 스프링 컨텍스트와 벗어나, DB 접근이 어려움
  1. 검증 이후 ThreadLocal 변수에 사용자 정보를 저장 => 컨트롤러에서 Thread Local을 조회하여 사용
  • RestController의 흐름(인자로 데이터를 획득하는 방식)과 맞지 않다고 생각됨.
  1. Interceptor에서 검증 이후 addAttribute로 사용자 정보 저장 => 컨트롤러에서 getAttribute를 이용하여 획득
  • RestController의 흐름과 맞지 않다고 생각됨.
  1. 컨트롤러의 인자로 사용자 정보를 선언 => ArgumentResolver에서 검증된 사용자를 컨트롤러의 인자에 할당
  • 인자로 전달하는 것이 가장 보기 좋고, 흐름에도 잘 맞는다고 생각 됨.

구현 방식

  1. xml 파일에 ArgumentResolver등록
  2. Controller의 메서드에서 사용자 정보를 인자로 사용함.
  3. Interceptor에서 검증에 사용한 사용자 정보를 attribute로 저장
  4. ArgumentResolver에서 attribute의 사용자 정보를 Controller 메서드 인자에 삽입함.
  • @Login 어노테이션이 붙은 요소만 사용
  1. Swagger 상에는 해당 인자를 ignore 시킴.

사용자 정보 DTO vs Domain

컨트롤러이지만, 도메인(User.java)를 사용하였다.

컨트롤러에서 DTO를 사용하는 이유는 API의 RequestBody로 들어오는 객체와 서비스 객체의 의존성을 분리하기 위함이라고 생각하는데, 사용자 정보의 경우 이미 검증 로직에 도메인으로 활용하기에 의존성을 줄이는 것이 의미가 없을거라 생각했다.

또한, 리졸버에서 UserDomain -> UserRequestDTO 로 변환 후 추후 다시 UserDomain을 변환하는 불필요한 반복작업이 생길 것이고, 도메인을 RequestDTO로 변환한다는 흐름도 흐름과 맞지않다 생각하였다.

아쉬운 점

인터셉터에서 사용자 아이디 뿐만이 아니라, DB의 사용자 자체를 사용하기에 (1. 탈퇴여부 확인 2. 여러 권한 검증) 리졸버의 역할이 크지 않게 되었다.

참고

0개의 댓글