앞 포스팅을 통해 JWT 기능 구현을 하였습니다. 이번 포스팅에서는 해당 코드가 제대로 동작하는 지 테스트 코드를 작성해보겠습니다.
JWT 테스트(1): TokenProvider
임의로 JWT를 생성하기 위한 mocking 객체인 JwtFactory를 생성합니다.
/**
* JWT 토큰 서비스를 테스트하기 위한 mocking 용 객체
*/
@Getter
public class JwtFactory {
private String subject = "test@gmail.com";
private Date issuedAt = new Date();
private Date expiration = new Date(new Date().getTime() + Duration.ofDays(14).toMillis());
private Map<String, Object> claims = emptyMap();
@Builder
public JwtFactory(String subject, Date issuedAt, Date expiration, Map<String, Object> claims) {
this.subject = subject != null ? subject : this.subject;
this.issuedAt = issuedAt != null ? issuedAt : this.issuedAt;
this.expiration = expiration != null ? expiration : this.expiration;
this.claims = claims != null ? claims : this.claims;
}
public static JwtFactory withDefaultValues() {
return JwtFactory.builder().build();
}
public String createToken(JwtProperties jwtProperties) {
return Jwts.builder()
.setSubject(subject)
.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
.setIssuer(jwtProperties.getIssuer())
.setIssuedAt(issuedAt)
.setExpiration(expiration)
.addClaims(claims)
.signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey())
.compact();
}
}
다음으로 TokenProvider 클래스를 테스트하는 클래스를 생성합니다.
@Slf4j
@SpringBootTest
class TokenProviderTest {
@Autowired private TokenProvider tokenProvider;
@Autowired private UserRepository userRepository;
@Autowired private JwtProperties jwtProperties;
@DisplayName("generateToken(): 유저 정보와 만료 기간을 전달해 토큰을 만들 수 있다.")
@Test
void generateToken() {
// given
User testUser = userRepository.save(User.builder()
.email("user@email.com")
.password("test")
.name("name")
.role(Role.USER)
.build());
// when
String token = tokenProvider.generateToken(testUser, Duration.ofDays(14));
log.info("token={}", token);
// then
Long userId = Jwts.parser()
.setSigningKey(jwtProperties.getSecretKey())
.parseClaimsJws(token)
.getBody()
.get("id", Long.class);
assertThat(userId).isEqualTo(testUser.getId());
}
@DisplayName("validToken(): 만료된 토큰일 때 유효성 검증에 실패한다.")
@Test
void validToken() {
// given
String token = JwtFactory.builder()
.expiration(new Date(new Date().getTime() - Duration.ofDays(7).toMillis()))
.build()
.createToken(jwtProperties);
// when
boolean result = tokenProvider.validToken(token);
// then
assertThat(result).isFalse();
}
@DisplayName("getAuthentication(): 토큰 기반으로 인증 정보를 가져올 수 있다.")
@Test
void getAuthentication() {
// given
String userEmail = "user@email.com";
String token = JwtFactory.builder()
.subject(userEmail)
.build()
.createToken(jwtProperties);
// when
Authentication authentication = tokenProvider.getAuthentication(token);
// then
assertThat(((UserDetails) authentication.getPrincipal()).getUsername()).isEqualTo(userEmail);
}
@DisplayName("getUserId(): 토큰으로 유저 ID를 가져올 수 있다.")
@Test
void getUserId() {
// given
long userId = 1L;
String token = JwtFactory.builder()
.claims(Map.of("id", userId))
.build()
.createToken(jwtProperties);
// when
Long userIdByToken = tokenProvider.getUserId(token);
// given
assertThat(userIdByToken).isEqualTo(userId);
}
}
테스트 코드가 잘 동작하는지 실행해보면 다음과 같이 작성해둔 테스트가 모두 성공하는 것을 확인할 수 있습니다.
JWT 테스트(2): TokenApiController
JWT 기능 테스트가 완료되었다면 TokenApiController를 통해 Access Token이 정상적으로 재발급 되는지 테스트하겠습니다. TokenApiController 클래스를 테스트하는 클래스를 생성합니다.
@Slf4j
@SpringBootTest
@AutoConfigureMockMvc
class TokenApiControllerTest {
@Autowired MockMvc mockMvc;
@Autowired ObjectMapper objectMapper;
@Autowired WebApplicationContext context;
@Autowired JwtProperties jwtProperties;
@Autowired UserRepository userRepository;
@Autowired RefreshTokenRepository refreshTokenRepository;
@BeforeEach
public void mockMvcSetUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
userRepository.deleteAll();
}
@DisplayName("createAccessToken(): 새로운 액세스 토큰을 발급한다.")
@Test
void createAccessToken() throws Exception {
// given
final String url = "/api/token";
User user = userRepository.save(User.builder()
.email("user@email.com")
.password("test")
.name("name")
.role(Role.USER)
.build());
String refreshToken = JwtFactory.builder()
.claims(Map.of("id", user.getId()))
.build()
.createToken(jwtProperties);
refreshTokenRepository.save(new RefreshToken(user.getId(), refreshToken));
CreateAccessTokenRequest request = new CreateAccessTokenRequest(refreshToken);
final String requestBody = objectMapper.writeValueAsString(request);
// when
ResultActions resultActions = mockMvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody));
// then
resultActions
.andExpect(status().isCreated())
.andExpect(jsonPath("$.accessToken").isNotEmpty());
}
}
테스트 코드 실행 시 Access Token을 정상적으로 발급하는 것을 확인할 수 있습니다.
다음으로
테스트를 마지막으로 JWT 토큰 기능 구현을 완료하였습니다!
토큰 기반 인증인 JWT를 처음 구현해봤기 때문에 순탄치만은 않은 과정이였던 것 같습니다. 덕분에 시간도 오래 걸렸지만 만들고 보니 좋은 경험이 되었습니다. 아직 OAuth 2.0과 Spring Security 설정이 남아 있으니 조금만 더 힘내봅시다!
이전 포스팅으로 이동
Spring Security + JWT + OAuth 2.0 회원 기능(2) - JWT 설정
다음 포스팅으로 이동
Spring Security + JWT + OAuth 2.0 회원 기능(4) - OAuth 2.0 개념
'Spring > Spring Security' 카테고리의 다른 글
Spring Security + JWT + OAuth 2.0 회원 기능(5) - OAuth 2.0 인증 서버 등록 (0) | 2024.03.11 |
---|---|
Spring Security + JWT + OAuth 2.0 회원 기능(4) - OAuth 2.0 개념 (1) | 2024.03.11 |
Spring Security + JWT + OAuth 2.0 회원 기능(2) - JWT 기능 구현 (1) | 2024.03.11 |
Spring Security + JWT + OAuth 2.0 회원 기능(1) - JWT 개념 (3) | 2024.03.11 |
Spring Security 6.1, xxx is deprecated and marked for removal (1) | 2024.02.26 |