1년 전 진행했던 마실가실 프로젝트를 🛠️리팩토링하며 정리한 내용입니다.
스마트 루피를 데려온 이유는 스마트하게 로그아웃을 완성했기 때문입니다.
JWT와 로그아웃(1) 게시물을 8월 23일에 작성하였는데, 약 3주의 시간이 흘렀습니다,,
사실 구현은 이전에 했었는데, 멋진 코드를 붙여넣다보니 내 것인듯 내 것 아닌 내 것 같은 코드가 되어버려서 리리팩토링도 하고, SonarQube 등 설치도 하고.. 스프링 공부도 하고.. 그랬습니다..
서론은 여기서 마치고, 로그아웃 코드를 정리해보도록 하겠습니다.
https://hj0216.tistory.com/939
[1년 후 마실가실] JWT와 로그아웃(1) Redis 설정
1년 전 진행했던 마실가실 프로젝트를 🛠️리팩토링하며 정리한 내용입니다. 마실가실의 숨겨진 비밀..한 번 로그인 한 고객님은.. 서버를 내리기 전까지 로그아웃할 수 없습니다. 이제는
hj0216.tistory.com
https://hj0216.tistory.com/945
[1년 후 마실가실] JWT와 로그아웃(2) RefreshToken
1년 전 진행했던 마실가실 프로젝트를 🛠️리팩토링하며 정리한 내용입니다. 이것저것 좋아보이는 코드를 붙여넣다보니, 이도저도 아닌 누구세요 코드가 되어 정리를 좀 했습니다,,이름하여
hj0216.tistory.com
UserController.java
@RestController
@RequestMapping("api/v2/users")
@RequiredArgsConstructor
public class UserController {
@PostMapping("/logout")
@ResponseStatus(HttpStatus.OK)
public void logout(@RequestBody TokenInfo logoutRequestDto){
userService.logout(logoutRequestDto);
}
}
UserService.java
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class UserService {
public void logout(TokenInfo logoutRequestDto) {
if (!jwtTokenProvider.isValidAccessToken(logoutRequestDto.getAccessToken())) {
throw new BusinessException(INVALID_ACCESS_TOKEN);
}
if (redisUtil.get("RT:" + logoutRequestDto.getRefreshToken()) != null) {
redisUtil.delete("RT:" + logoutRequestDto.getRefreshToken());
}
Long expiration = jwtTokenProvider.getExpiration(logoutRequestDto.getAccessToken());
redisUtil.setBlackList("AT:" + logoutRequestDto.getAccessToken(), "logout", expiration);
}
}
1. 로그아웃 요청 전 Access Token의 유효성 검증
2. Refresh Token을 통한 Access Token 재발급을 막기 위해 Redis에 저장된 Refresh Token이 있으면 삭제
3. Redis에 AccessToken을 BlackList 방식으로 저장하여 해당 토큰을 기반으로 한 Login 방지
* BlackList 방식
특정 토큰을 무효화하기 위해 그 토큰을 블랙리스트에 추가하는 것
JwtTokenProvider.java
@Component // Spring이 이 클래스를 자동으로 스캔하고 빈으로 등록
@RequiredArgsConstructor
public class JwtTokenProvider {
// 토큰 정보를 검증하는 메서드
public boolean isValidAccessToken(String accessToken) {
if(redisUtil.hasKeyBlackList("AT:" + accessToken)){
return false;
}
try {
parseClaims(accessToken);
return true;
} catch (MalformedJwtException e) {
throw new BusinessException(MALFORMED_JWT);
} catch (ExpiredJwtException e) {
throw new BusinessException(EXPIRED_JWT);
} catch (UnsupportedJwtException e) {
throw new BusinessException(UNSUPPORTED_JWT);
} catch (IllegalStateException e) {
throw new BusinessException(ILLEGAL_STATE_JWT);
}
}
private Claims parseClaims(String token) {
Key secretKey = Keys.hmacShaKeyFor(jwtSecretKey.getBytes(StandardCharsets.UTF_8));
return Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody();
}
public Long getExpiration(String token) {
Key secretKey = Keys.hmacShaKeyFor(jwtSecretKey.getBytes(StandardCharsets.UTF_8));
// Token 남은 유효시간
Date expiration = Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody()
.getExpiration();
// 현재 시간
Long now = new Date().getTime();
return (expiration.getTime() - now); // TimeUnit.MILLISECONDS
}
}
Access Token의 유효성 검사를 통해
* 로그아웃한 사용자인지 확인
* token을 통해 claims(JWT 를 이용해 전송되는 암호화된 정보) 추출 시, Exception 발생 여부 확인
JwtAuthenticationFilter.java
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
// JWT 토큰의 인증 정보를 현재 쓰레드의 SecurityContext에 저장하는 역할 수행
@Override
public void doFilterInternal(HttpServletRequest request
, HttpServletResponse response
, FilterChain filterChain
) throws IOException, ServletException {
String accessToken = SecurityUtils.resolveToken(request);
if (accessToken != null) {
if(jwtTokenProvider.isValidAccessToken(accessToken)){
Authentication authentication = jwtTokenProvider.getAuthentication(accessToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
throw new BusinessException(LOGOUT_MEMBER);
}
}
filterChain.doFilter(request, response);
}
}
Filter에서 AccessToken의 유효성 검사를 수행하도록 하여 Logout한 멤버인지 확인
⭐ 좀 더 생각해봐야 할 부분 ⭐
* 필터에 유효성 검사를 추가하면 무조건 검사가 진행되는데, JwtTokenProvider에 추가적으로 수행한다면 중복 로직이 아닐까?
🙋♀️
본 포스트는 공부 목적으로 작성하였습니다.
보시는 도중 잘못된 부분이나 개선할 부분이 있다면 댓글로 알려주시면 수정하도록 하겠습니다.
📑
참고 자료
https://slimeland.tistory.com/41
[개발]JWT 갱신 토큰 관리 방법(5)-블랙리스트 사용
JWT (JSON Web Tokens) 인증 시스템에서 토큰을 무효화하는 방법 중 하나는 블랙리스트(blacklist)를 사용하는 것입니다. 이 방법은 특정 토큰을 무효화하기 위해 그 토큰을 블랙리스트에 추가하는 것을
slimeland.tistory.com
https://jake-seo-dev.tistory.com/77
JWT(Json Web Token) 이란 무엇이며 왜 사용하는가?
JWT (JSON Web Token) 란 무엇인가? open standard 로 RFC 7519 에 표준에 관한 내용이 기재되어 있다. 통신에 JSON 을 이용하여 JSON 객체를 통해 두 당사자 간 정보를 보안이 적용된 안전한 방식으로 전달한다.
jake-seo-dev.tistory.com
'PlayGround > 마실가실 리팩토링' 카테고리의 다른 글
[1년 후 마실가실] Spring Security 특정 url 제외 (1) | 2024.09.18 |
---|---|
[1년 후 마실가실] Custom Error 처리 (0) | 2024.09.17 |
[1년 후 마실가실] JWT와 로그아웃(2) RefreshToken (1) | 2024.09.14 |
[1년 후 마실가실] 쉬어가는 마실가실 - SonarQube (2) | 2024.09.08 |
[1년 후 마실가실] 패키지 구조 (3) | 2024.09.03 |