밤빵's 개발일지
[WIL]20240728 리프레시토큰은 어렵다. 본문
JWT와 로그인... 구현하면서 GPT로 해결되지않는 문제에 너무 많은 시간을 소비했다. 리서치하고, 유튜브 강의 다시보고 예시자료 참고하고 여러 방법을 진행해봤지만 도저히 해결방법이 나오지않았고, 기술매니저님께 받은 조언으로 강의내용과 같은 버전으로 생각없이 바꿔서 진행이 되간다고 좋아하다가 다시 12.5로 돌아오면서 또 다시 같은 문제 시작...!
🥺리프레시 토큰과 로그인 구현
이번 개발일지에서는 JWT(JSON Web Token)를 사용한 로그인 구현에서 중요한 리프레시 토큰(Refresh Token)의 개념과 이를 통한 로그인 유지 및 보안 강화를 중심으로 정리해봤다. 팀 프로젝트에서 JWT와 로그인 기능을 처음으로 구현하며 겪은 문제점과 해결 방법에 대해 간략하게 정리하고, 팀장님이 작성한 코드와 내코드를 비교하며 수정을 시작했다.
▶ 리프레시 토큰(Refresh Token)이란?
리프레시 토큰은 JWT를 사용한 인증 시스템에서 중요한 역할을 한다. 일반적으로 액세스 토큰(Access Token)은 만료 시간이 짧고, 사용자 인증 정보를 포함하여 클라이언트가 서버에 요청할 때마다 보내는 토큰이다. 반면, 리프레시 토큰은 액세스 토큰이 만료되었을 때 새로운 액세스 토큰을 발급받기 위해 사용하는 토큰으로, 만료 시간이 더 길다. 리프레시 토큰은 주로 클라이언트의 보안이 보장되는 저장소에 안전하게 저장되며, 액세스 토큰보다 서버와의 상호작용이 적다. 리프레시 토큰을 사용하는 이유는 다음과 같다:
→ 보안성 강화:
액세스 토큰이 만료되면, 더 이상 사용할 수 없기 때문에 잠재적인 보안 위협을 줄일 수 있다.
→ 세션 유지:
리프레시 토큰을 사용하면 사용자가 다시 로그인하지 않고도 세션을 유지할 수 있어 사용자 경험이 개선된다.
→ 유효기간 관리:
리프레시 토큰과 액세스 토큰의 유효 기간을 다르게 설정함으로써 인증과 보안의 균형을 맞출 수 있다.
▶ 로그인 구현 방식
로그인 구현에서 중요한 부분은 사용자의 인증 상태를 유지하는 방법이다. JWT를 사용하여 사용자가 로그인할 때, 서버는 액세스 토큰과 리프레시 토큰을 발급한다. 클라이언트는 서버로부터 받은 액세스 토큰을 사용해 인증된 요청을 수행한다. 액세스 토큰이 만료되면, 클라이언트는 리프레시 토큰을 사용해 새로운 액세스 토큰을 발급받아 지속적으로 인증된 상태를 유지할 수 있다. 이 과정은 다음과 같이 이루어진다:
→ 로그인 요청:
사용자가 ID와 비밀번호로 로그인하면 서버는 사용자의 자격 증명을 확인한다.
→ 토큰 발급:
인증에 성공하면, 서버는 액세스 토큰과 리프레시 토큰을 생성하여 클라이언트에 전달한다.
→ 토큰 저장:
클라이언트는 액세스 토큰과 리프레시 토큰을 저장한다. 액세스 토큰은 주로 메모리나 쿠키에 저장하고, 리프레시 토큰은 보안이 강화된 저장소(예: HttpOnly 쿠키 또는 안전한 스토리지)에 저장한다.
→ 인증된 요청:
클라이언트는 액세스 토큰을 사용하여 서버에 인증된 요청을 보낸다.
→ 토큰 갱신:
액세스 토큰이 만료되면, 리프레시 토큰을 사용하여 서버에 새로운 액세스 토큰을 요청한다.
→ 로그아웃:
사용자가 로그아웃하면 서버는 리프레시 토큰을 폐기하거나 무효화하여 보안을 유지한다.
▶ 코드 분석
서로 다른 부분이지만 팀장님께 피드백을 받고 서로 코드를 리뷰했다.
→ 저 parserBuilder가 자꾸 저런식으로 에러가 떠서 GPT닦달하고 그랬는데 자꾸 같은 답변 여러개를 돌려서 알려줘서 팀장님께 조언을 구했다. 하나되면 그 다음이 빨간줄이 뜨고.. 대체 해결방법이 뭘까😢
→ 팀장님 코드. 서진님의 피드백은 GPT의존도를 낮추고 Ctrl + class 이름 으로 들어가서 그 내용을 따라한다는 생각으로 코드를 구현해보라고 하셨다.
▷ 팀장님 코드 : 액세스 토큰과 리프레시 토큰 생성 메서드
public String createAccessToken(String username) {
Date now = new Date();
return Jwts.builder()
.claim("email", username)
.issuedAt(now)
.expiration(new Date(now.getTime() + accessTokenValidTime))
.signWith(secretKey)
.compact();
}
public String createRefreshJwtToken(String username) {
Date now = new Date();
return Jwts.builder()
.claim("username", username)
.issuedAt(now)
.expiration(new Date(now.getTime() + refreshTokenValidTime))
.signWith(secretKey)
.compact();
}
→ 이 두 메서드는 각각 엑세스 토큰과 리프레시 토큰을 생성하는 기능을 한다.
createAccessToken 메서드는 사용자 이름(또는 이메일)을 포함하는 액세스 토큰을 생성하며, 만료 시간은 짧다. createRefreshJwtToken 메서드는 사용자 이름을 포함한 리프레시 토큰을 생성하며, 만료 시간이 더 길다. 이 두 메서드는 토큰에 포함될 클레임과 발급 시간, 만료 시간을 설정하고, 서명 키를 사용해 토큰을 서명한 후, 최종적으로 압축된 JWT 토큰을 반환한다. ( 스크린샷에서의 내 코드와 다른 부분이지만 내가 작성한 코드는 저 메서드에도 같은 에러를 겪고있다..ㅠㅠ)
▽ GPT가 알려준 내 코드 해결방법 : 토큰 검증 및 파싱 메서드
public static String getEmailFromToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public static boolean validateToken(String token) {
try {
Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
→ getEmailFromToken 메서드는 JWT 토큰을 파싱하여 클레임에서 이메일을 추출한다. validateToken 메서드는 JWT 토큰이 유효한지 검증하며, 서명 키를 사용해 서명이 올바른지 확인한다. 두 메서드 모두 JWT 라이브러리를 사용하며, 잘못된 토큰일 경우 예외를 처리하도록 구성되어 있다.
▶ 정리
오늘 개발일지에서는 JWT를 활용한 로그인 구현에서 액세스 토큰과 리프레시 토큰의 개념과 사용 방법을 다뤘다. 액세스 토큰은 짧은 만료 시간으로 보안성을 높이며, 리프레시 토큰은 더 긴 만료 시간으로 사용자 경험을 향상시킨다. 이 두 토큰을 함께 사용함으로써 보안성과 편의성을 모두 만족하는 인증 시스템을 구현할 수 있다. 각기 다른 팀원이 작성한 코드를 하나의 클래스에 결합하면서 발생할 수 있는 통합 문제를 해결하기 위해 코드의 일관성과 기능을 명확히 정의하는 것이 중요하다는 점도 깨달았다. 이 일관성을 지키고 싶어서 열심히 에러를 해결하는 중인데 계속 도돌이표ㅠㅠㅠ
'개발Article' 카테고리의 다른 글
[TIL]20240730 Bcrypt (0) | 2024.07.30 |
---|---|
[TIL]20240729 jwtUtil은 순수하다 (0) | 2024.07.30 |
[TIL]20240727 JPA의 트랜잭션 (0) | 2024.07.27 |
[TIL]20240726 영속성 컨텍스트(Persistence) (0) | 2024.07.26 |
[TIL]20240725 님버스?.. 왜 내 토큰은 파싱이 안되는걸까..? (0) | 2024.07.26 |