JWT 란
JWT(Json Web Token) 는 Json 형식을 이용하여 사용자에 대한 속성을 저장하는 Web Token이다.
JWT 에 관한 정보는 여기에 정리해두었다.
JWT 를 이용한 인증 1
최근 많이 쓰이는 JWT 를 이용해 DB 조회 없이 인증 로직을 만들어볼것이다.
기존의 인증 및 검증 과정은 DB 가 필요했지만 JWT 는 DB에서 확인하는 과정이 필요 없다는 장점이 있다.
인증 과정은 아래와 같다.
- 로그인 성공 시 JWT 를 만들어서 응답으로 JWT 를 발급
- 인증이 필요한 페이지에 요청 시 Authorization 헤더에 담긴 JWT 를 확인하여 인증
1. 로그인 성공 시 JWT 를 만들어서 응답으로 JWT 를 발급
이 글에서는 JWT를 만들고 응답에 넣어서 보내는것 까지만 설명한다.
인증을 처리하는 과정이 궁금하다면 다음 글을 확인하자.
AuthController
SecretKey 를 Application.yml 에 설정해두고 AppConfig 에서 이 설정 정보를 읽어온다.
이 AppConfig 에서 SecretKey를 꺼내와서 토큰 발급 및 검증에 사용한다.
application.yml 에서 프로퍼티 정보를 가져오는건 이 글에 정리해두었다.
로그인 성공 시 사용자의 이름과 발급 시간 정보가 담긴 JWT 토큰을 응답값에 담아 반환한다.
Claim 에 토큰 수명을 설정할수도 있다.
@Slf4j
@RestController
@RequiredArgsConstructor
public class AuthController {
private final AuthService authService;
private final AppConfig appConfig;
@PostMapping("/auth/login")
public SessionResponse login(@RequestBody LoginRequest request) {
String name = authService.signin(request);
SecretKey secretKey = Keys.hmacShaKeyFor(appConfig.getJwtKey());
// 토큰을 응답
return new SessionResponse(Jwts.builder()
.subject(name)
.issuedAt(new Date())
.signWith(secretKey)
.compact());
}
}
application.yml
midcon:
jwt-key: "pjx7jVXbdaeOmw0ZO1SotIHLVApe8FZ+LmGCuMKa8T8="
AppConfig
AppConfig 에서 jwtKey 정보를 가져올때 굳이 인코딩 된 jwtKey 를 가져올 필요가 없으므로 AppConfig 에서 디코딩 한다.
@RequiredArgsConstructor
@ConstructorBinding
@ConfigurationProperties(prefix = "midcon")
public class AppConfig {
private final String jwtKey;
public byte[] getJwtKey() {
return Decoders.BASE64.decode(jwtKey);
}
}
AuthService
로그인 성공 시 유저 이름을 반환하도록 수정했다.
@Service
@RequiredArgsConstructor
public class AuthService {
private final UserRepository userRepository;
@Transactional
public String signin(LoginRequest request) {
User user = userRepository.findByEmailAndPassword(request.getEmail(), request.getPassword())
.orElseThrow(() -> new InvalidSigninInformationException());
return user.getName();
}
}
테스트
AuthServiceTest
서비스 로직에 변화가 생겨서 메서드가 반환한 유저 이름을 확인하는 테스트로 수정한다.
@DisplayName("로그인 요청 시 DB 정보와 일치하면 accessToken 을 발급한다.")
@Test
void signin() throws JsonProcessingException {
// given
LoginRequest request = LoginRequest.builder()
.email("hyukkind@naver.com")
.password("1234")
.build();
// when
String name = authService.signin(request);
User user = userRepository.findByEmailAndPassword(request.getEmail(), request.getPassword())
.orElseThrow(() -> new InvalidSigninInformationException());
// then
assertEquals(user.getName(), name);
}

테스트는 통과하고 의도했던대로 동작함을 알 수 있다.
AuthControllerTest
로그인의 성공, 실패 테스트를 실행하고 body 에 JWT 가 응답으로 내려왔는지 확인하는 테스트를 작성한다.
Mockito 를 이용하여 JWT 토큰 값을 임의로 정해서 확인하는 테스트도 만들 수 있을것같지만 추후에 해보도록 하자.
@DisplayName("로그인 성공")
@Test
void login() throws Exception {
// given
LoginRequest request = LoginRequest.builder()
.email("hyukkind@naver.com")
.password("1234")
.build();
String json = objectMapper.writeValueAsString(request);
// expected
mockMvc.perform(
post("/auth/login")
.contentType(APPLICATION_JSON)
.content(json)
)
.andDo(print())
.andExpect(status().isOk());
}
@DisplayName("로그인 실패")
@Test
void login2() throws Exception {
// given
LoginRequest request = LoginRequest.builder()
.email("hyukkind@naver.com")
.password("1111")
.build();
String json = objectMapper.writeValueAsString(request);
// expected
mockMvc.perform(
post("/auth/login")
.contentType(APPLICATION_JSON)
.content(json)
)
.andDo(print())
.andExpect(status().isBadRequest());
}

테스트는 성공하고 원하는대로 JWT 또한 제대로 응답에 담겨서 내려오는걸 알 수 있다.
글이 길어져서 다음 글에서 이어서 작성한다.
'API 만들어 보기 > 게시판 API' 카테고리의 다른 글
| [API 인증] 회원가입과 비밀번호 암호화 (0) | 2023.10.09 |
|---|---|
| [API 인증] JWT 를 이용한 인증 2 (0) | 2023.10.07 |
| [API 인증] 쿠키를 통한 인증 및 검증 (0) | 2023.10.03 |
| [API 인증] DB를 통한 토큰 발급 및 검증 2 (0) | 2023.10.02 |
| [API 인증] DB를 통한 토큰 발급 및 검증 1 (0) | 2023.10.01 |