오늘은 JWT를 저장하면서 클라이언트 정보에 따라 다르게 DB에 저장하도록 하는 기능을 해보자!
구현을 하는 이유는 Restful API를 구현하면서 클라이언트에 따른 Refresh Token을 구분해주어야 하기 때문이다. 사용자는 웹, 모바일, 기타 기기 등으로 해당 서비스를 접속할 수 있기때문에 사용자의 계정 1개에 따라 여러개의 토큰을 발급 받아야 한다. 이전까지 구현된 내용은 계정 1개당 1개의 Refresh Token만 발급되도록 되어있다.
가장 먼저, 해당 기능을 구현할 branch를 생성하자.
git flow feature start JwtClient
git checkout JwtClient
먼저 Request로 날라올 Client 정보를 조회해보자.
@GetMapping("/jwtTest")
public String jwtTest(@RequestHeader("User-Agent") String userAgent){
log.info("UserAgent = {}", userAgent);
return userAgent;
}
먼저 Get으로 매핑하고, Header값의 "User-Agent"값을 받아오자.
그리고 여러가지 값으로 요청해보자.
이건 pc의 local로 동작하기 때문에 나도 모른다.. ㅋㅋ
이제 User-Agent에 따라 다른 Refresh Token을 저장해보자.
이를 위해서는 해야할 일이 있다.
1. "User-Agent"값 받아오기
2. "User-Agent"값 구분하기
3. 구분된 문자열로 DB에 구분하여 저장
위에서 해보았지만 본격적으로 코드에 적용하자.
이전 글에서 보았듯이, "/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로 값을 넘겨주자.
해당 내용은 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);
}
저장되어 있으면 삭제 후, 새로 저장
저장되어 있지 않으면 바로 저장
포스트맨을 이용하여 테스트해보자.
두 가지 요청 모두 올바르게 응답을 받았다. DB에는 agent에 따른 Refresh Token이 2개 저장되어야 한다.
성공!
sequence id가 2, 4인 이유는 같은 요청으로 여러번 눌렀기 때문이다.
이제 깃에 풀리퀘 올리면 된다.
JWT 글 보고 많은 도움을 얻었습니다..! 좋은 포스트 감사합니다..!