[JWT] Refresh Token 클라이언트정보 저장하기

YoungHo-Cha·2022년 3월 6일
3

운동 매칭 시스템

목록 보기
6/17

오늘은 JWT를 저장하면서 클라이언트 정보에 따라 다르게 DB에 저장하도록 하는 기능을 해보자!

🔍목차

  • 구현 이유
  • 클라이언트 정보 가져오기
  • 클라이언트 정보에 따른 Refresh 토큰 저장
  • 테스트

📌구현 이유

구현을 하는 이유는 Restful API를 구현하면서 클라이언트에 따른 Refresh Token을 구분해주어야 하기 때문이다. 사용자는 웹, 모바일, 기타 기기 등으로 해당 서비스를 접속할 수 있기때문에 사용자의 계정 1개에 따라 여러개의 토큰을 발급 받아야 한다. 이전까지 구현된 내용은 계정 1개당 1개의 Refresh Token만 발급되도록 되어있다.

내가 바라는 것

  1. 같은 agent로 요청 시, Refresh 토큰은 중복 발급되면 안된다.
  2. agent에 따른 Refresh 토큰은 DB에 저장되어야 한다.

📌클라이언트 정보 가져오기

branch 생성

가장 먼저, 해당 기능을 구현할 branch를 생성하자.

git flow feature start JwtClient

git checkout JwtClient

Request 분석

먼저 Request로 날라올 Client 정보를 조회해보자.

TestController 작성


@GetMapping("/jwtTest")
    public String jwtTest(@RequestHeader("User-Agent") String userAgent){
        log.info("UserAgent = {}", userAgent);

        return userAgent;
    }

먼저 Get으로 매핑하고, Header값의 "User-Agent"값을 받아오자.
그리고 여러가지 값으로 요청해보자.

  1. PostMan 요청

  1. Mac - Chrome 요청

  1. Mobile 요청

이건 pc의 local로 동작하기 때문에 나도 모른다.. ㅋㅋ

이제 User-Agent에 따라 다른 Refresh Token을 저장해보자.


📌클라이언트 정보에 따른 Refresh Token 저장

이를 위해서는 해야할 일이 있다.
1. "User-Agent"값 받아오기
2. "User-Agent"값 구분하기
3. 구분된 문자열로 DB에 구분하여 저장

"User-Agent"값 받아오기

위에서 해보았지만 본격적으로 코드에 적용하자.

이전 글에서 보았듯이, "/login" url에 토큰이 발급된다. 이를 수정하자.

UserController.java 코드 수정

// 로그인
    @PostMapping("/login")
    public Token login(@RequestBody Map<String, String> user, @RequestHeader("User-Agent") String userAgent) {
        log.info("user email = {}", user.get("userEmail"));
        User member = userRepository.findByUserEmail(user.get("userEmail"))
                .orElseThrow(() -> new IllegalArgumentException("가입되지 않은 E-MAIL 입니다."));
       // return jwtService.login(member);
        Token tokenDto = jwtTokenProvider.createAccessToken(member.getUsername(), member.getRoles());
       
       // 여기서 user agent를 넘겨주어야함
        jwtService.login(tokenDto, userAgent);
        return tokenDto;
    }
  • "User-Agent"를 받아왔다.

  • "userService"에서 refresh token 검증을 하니까 service로 값을 넘겨주자.

"User-Agnet"값 구분하기

해당 내용은 DB에 해당 agent값으로 저장되어있는지 확인하는 내용이다.

RefreshTokenRepository.java

토큰의 agent에 따른 정보를 확인하는 jpa 메소드를 생성해야한다.

public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
   Optional<RefreshToken> findByRefreshToken(String refreshToken);
   
   //생성
   boolean existsByKeyEmailAndUserAgent(String userEmail, String userAgent);
   
   //생성
   void deleteByKeyEmailAndUserAgent(String userEmail, String userAgent);
}

RefreshToken.java

토큰 도메인도 수정해야한다. 토큰 도메인은 agent값을 가지고 있어야 한다.

public class RefreshToken {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "REFRESH_TOKEN_ID", nullable = false)
    private Long refreshTokenId;

    @Column(name = "REFRESH_TOKEN", nullable = false)
    private String refreshToken;

    @Column(name = "KEY_EMAIL", nullable = false)
    private String keyEmail;

    @Column(name ="USER_AGENT", nullable = false)
    private String userAgent;

}

UserService.java

이제 구현된 내용을 토대로 service에서 check를 하도록 하자.


@Transactional
    public void login(Token tokenDto, String userAgent){

        RefreshToken refreshToken = RefreshToken.builder().keyEmail(tokenDto.getKey()).refreshToken(tokenDto.getRefreshToken()).build();
        String loginUserEmail = refreshToken.getKeyEmail();
        //여기서 refresh Token과 해당 agent로 저장되어있는지 확인을 해야한다.
        if(refreshTokenRepository.existsByKeyEmailAndUserAgent(loginUserEmail, userAgent)){
            log.info("기존의 존재하는 refresh 토큰 삭제");
            refreshTokenRepository.deleteByKeyEmailAndUserAgent(loginUserEmail, userAgent);
        }
        refreshTokenRepository.save(refreshToken);

    }

저장되어 있으면 삭제 후, 새로 저장
저장되어 있지 않으면 바로 저장


📌테스트

포스트맨을 이용하여 테스트해보자.

  • agent - postman

  • agnet - chrome

두 가지 요청 모두 올바르게 응답을 받았다. DB에는 agent에 따른 Refresh Token이 2개 저장되어야 한다.

성공!

sequence id가 2, 4인 이유는 같은 요청으로 여러번 눌렀기 때문이다.

이제 깃에 풀리퀘 올리면 된다.

profile
관심많은 영호입니다. 궁금한 거 있으시면 다음 익명 카톡으로 말씀해주시면 가능한 도와드리겠습니다! https://open.kakao.com/o/sE6T84kf

1개의 댓글

comment-user-thumbnail
2024년 2월 18일

JWT 글 보고 많은 도움을 얻었습니다..! 좋은 포스트 감사합니다..!

답글 달기