Spring/Security
Spring Security + JWT (RefreshToken, AccessToken)를 사용한 로그인, 로그아웃 구현 - 3편
짱엉
2024. 7. 22. 16:44
728x90
개요
앞서 1편에서 기본 파일 구조와 기본 세팅을 했고, 2편에서 security 보안 설정을 했다.
이번 3편에서는 jwt 패키지에 대해서 작성할 것이다.
일단.. 엔티티, 레포지토리, 서비스를 먼저 만들어 주자.
<- jwt의 대략적인 파일 구조
1. 엔티티 생성
- RefreshToken
@Entity
@Table(name = "refresh_token")
@Getter
@Setter
public class RefreshToken {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // 리프레시 토큰의 고유 식별자
@Column(name = "user_id")
private Long userId; // 이 리프레시 토큰과 연관된 사용자의 식별자
private String value; // 리프레시 토큰의 실제 값 -> 이 값은 서버가 클라이언트의 요청을 인증하는 데 사용
}
- 위 엔티티는 주로 액세스 토큰의 재발급을 위한 리프레시 토큰을 저장하는 데 사용된다.
Q. 리프레시 토큰이 뭔데?
A. 리프레시 토큰은 일반적으로 액세스 토큰보다 긴 유효 기간을 가지도록 설정하며 엑세스 토큰이 만료가 되면 클라이언트는 리프레시 토큰을 사용하여 새로운 액세스 토큰을 요청한다.
즉, 서버는 리프레시 토큰을 검증하고, 유효한 경우 새로운 액세스 토큰을 발급한다.
- JwtBlacklist
@Entity
@Table(name = "jwt_blacklist")
@Getter
@Setter
@NoArgsConstructor
public class JwtBlacklist {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // 블랙리스트 항목의 고유 식별자
private String token; // 블랙리스트에 포함된 JWT 문자열
@Column(name = "expiration_time")
private Date expirationTime; // 블랙리스트에 있는 JWT의 만료 시간
public JwtBlacklist(String token, Date expirationTime) {
this.token = token;
this.expirationTime = expirationTime;
}
}
- 사용자가 로그아웃하거나 토큰이 만료된 경우, 해당 토큰을 블랙리스트에 추가하여(이 때 token 필드에 JWT 문자열을 저장) 더 이상 사용되지 않도록 한다.
- JWT 인증 필터에서 요청이 들어올 때, 토큰이 블랙리스트에 있는지 확인하여 유효성을 검사한다. (token 필드를 사용하여 검증 수행)
- 블랙리스트에 있는 토큰은 서버에서 인식할 수 없으며, 인증 요청 시 해당 토큰이 블랙리스트에 포함되어 있으면 인증을 거부한다.
Q. 왜 블랙리스트를 써야하나?
- 사용자가 로그아웃을 요청했을 때, 클라이언트 측에서는 캐시에 저장된 jwt를 제거하지만 서버에서는 jwt를 무력화할 수단이 없다.
- 때문에 서버에서는 Jwt에 대한 블랙 리스트를 만들어 Jwt의 유효기간이 만료될 때 까지 Redis와 같은 DB에서 관리하는것을 말한다.
- 만약 사용자가 Jwt를 이용하여 인가를 요청했을 시, Black List의 조회를 통해 사용자가 로그아웃한 Jwt인지 아닌지를 판별하는 것이다.
2. Repository 설정
- RefreshTokenRepository
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
Optional<RefreshToken> findByValue(String value); // 리프레시 토큰 값으로 RefreshToken 엔티티 조회
boolean existsByValue(String token); // 리프레시 토큰 값이 데이터베이스에 존재하는지 여부
Optional<RefreshToken> findByUserId(Long userId); // 사용자 ID로 RefreshToken 엔티티 조회
}
- JwtBlacklistRepository
public interface JwtBlacklistRepository extends JpaRepository<JwtBlacklist, Long> {
boolean existsByToken(String token); // JWT 토큰이 블랙리스트에 존재하는지 여부 확인
}
각 메서드에 대해서 주석으로 설명함
3. Service 설정
- RefreshTokenService
@Service
@RequiredArgsConstructor
public class RefreshTokenService {
private final RefreshTokenRepository refreshTokenRepository;
// 리프레시 토큰 추가
@Transactional
public RefreshToken addRefreshToken(RefreshToken refreshToken) {
return refreshTokenRepository.save(refreshToken);
}
// 리프레시 토큰 조회
@Transactional
public Optional<RefreshToken> findRefreshToken(String refreshToken) {
return refreshTokenRepository.findByValue(refreshToken);
}
// 리프레시 토큰 삭제
public void deleteRefreshToken(String refreshToken) {
refreshTokenRepository.findByValue(refreshToken).ifPresent(refreshTokenRepository::delete);
}
// 사용자 기반 리프레시 토큰 삭제
public void deleteRefreshToken(Long userId) {
refreshTokenRepository.findByUserId(userId).ifPresent(refreshTokenRepository::delete);
}
// 리프레시 토큰 유효성 검증
public boolean isRefreshTokenValid(String refreshToken) {
return refreshTokenRepository.existsByValue(refreshToken);
}
}
- RefreshTokenService는 리프레시 토큰(Refresh Token)을 관리하는 데 관련된 기능을 제공한다.
- 리프레시 토큰은 사용자가 인증된 상태를 유지하도록 돕는 역할을 하고 새로운 액세스 토큰을 발급받기 위해 사용된다.
관련 메서드
- 리프레시 토큰 추가 (addRefreshToken)
- RefreshToken 객체를 데이터베이스에 저장하여 새로운 리프레시 토큰을 추가한다.
- 이 메서드는 리프레시 토큰을 발급하고 저장하는 데 사용된다.
- 리프레시 토큰 조회 (findRefreshToken)
- 주어진 리프레시 토큰 값을 사용하여 데이터베이스에서 해당 리프레시 토큰을 찾는다.
- 리프레시 토큰이 유효한지 확인하고 관련 정보를 반환하는 데 사용된다.
- 리프레시 토큰 삭제 (deleteRefreshToken)
- 주어진 리프레시 토큰 값을 사용하여 데이터베이스에서 해당 리프레시 토큰을 삭제한다.
- 사용자가 로그아웃하거나 토큰을 무효화할 때 사용된다.
- 사용자 기반 리프레시 토큰 삭제 (deleteRefreshToken(Long userId))
- 특정 사용자 ID에 연관된 리프레시 토큰을 삭제한다.
- 사용자가 로그아웃하거나 리프레시 토큰을 관리할 때 사용된다.
- 리프레시 토큰 유효성 검증 (isRefreshTokenValid)
- 주어진 리프레시 토큰이 데이터베이스에 존재하는지 확인하여 유효성을 검증한다.
- 유효한 리프레시 토큰인지 여부를 반환한다.
- JwtBlacklistService
@Service
@RequiredArgsConstructor
public class JwtBlacklistService {
private final JwtBlacklistRepository jwtBlacklistRepository;
// 블랙리스트에 추가
public void save(JwtBlacklist blacklist) {
jwtBlacklistRepository.save(blacklist);
}
// 블랙리스트 저장
public void addToBlacklist(String token, Date expirationDate) {
JwtBlacklist blacklist = new JwtBlacklist(token, expirationDate);
jwtBlacklistRepository.save(blacklist);
}
// 토큰 블랙리스트 확인
public boolean isTokenBlacklisted(String token) {
return jwtBlacklistRepository.existsByToken(token);
}
}
- JwtBlacklistService는 블랙리스트에 관리되는 JWT의 기능을 제공하여, 사용자가 로그아웃하거나 특정 JWT를 무효화할 때 사용하는 서비스이다.
- 블랙리스트는 특정 JWT가 더 이상 유효하지 않다는 것을 기록한다.
관련 메서드
- 블랙리스트에 추가 (addToBlacklist)
- 주어진 JWT와 만료 시간을 사용하여 새로운 JwtBlacklist 항목을 생성하고, 이를 데이터베이스에 저장한다.
- 이 메서드는 특정 JWT를 블랙리스트에 추가하여 더 이상 유효하지 않게 만든다.
- 블랙리스트 저장 (save):
- JwtBlacklist 객체를 데이터베이스에 저장한다.
- 이 메서드는 블랙리스트 항목을 생성하고 저장하는 데 사용된다..
- 토큰 블랙리스트 확인 (isTokenBlacklisted):
- 주어진 JWT가 블랙리스트에 포함되어 있는지 확인한다.
- 이 메서드는 JWT가 블랙리스트에 있는 경우 true를 반환하여, 해당 토큰이 더 이상 유효하지 않음을 알린다.
글이 길어져서 jwt 관련해서는 4편에서 이어서 쓰겠다. 4편에서....
728x90