SpringBoot JWT 로그인 구현 가이드
이전 글들에 이어서 JWT와 Swagger를 활용한 JWT 인증 예제를 구현해 보겠습니다. 처음 JWT를 구현할 때는 조급하게 구현을 진행했지만, 이번에는 JWT가 어떻게 Spring Boot에 통합되고, 토큰을 어떻게 검증하고 발급하는지에 대한 내용을 보다 자세히 다루려고 합니다.
이번 글에서는 AccessToken과 RefreshToken을 활용하여 토큰을 발급하고, RefreshToken의 검증을 통해 AccessToken을 재발급하는 방법에 대해 작성해 보겠습니다.
사용자 인증 컨트롤러 구현
@RestController
@RequestMapping("/accounts")
@RequiredArgsConstructor
public class AccountController {
private final AccountService accountService;
@Operation(summary = "회원 가입")
@PostMapping(value = "/sign-up")
public ResponseEntity<JwtTokenResBody> signUp(@Valid @RequestBody SignUpReqBody reqBody) {
return ResponseEntity.ok(accountService.signUp(reqBody));
}
@Operation(summary = "로그인")
@PostMapping(value = "/login")
public ResponseEntity<JwtTokenResBody> login(@Valid @RequestBody LoginReqBody reqBody) {
return ResponseEntity.ok(accountService.login(reqBody));
}
@Operation(summary = "리프레시 토큰 확인 발급")
@PostMapping(value = "/refresh-token")
public ResponseEntity<JwtTokenResBody> checkRefreshToken(@Valid @RequestBody RefreshTokenReqBody reqBody) throws BadRequestException {
return ResponseEntity.ok(accountService.checkRefreshToken(reqBody));
}
}
AccountController를 통하여 사용자의 회원가입, 로그인, 토큰 재발급에 대한 API를 생성했습니다.
각 API마다 필요한 회원 정보들을 받도록 구성하고, 성공 응답 시에 AccessToken과 RefreshToken을 받도록 구현하였습니다.
이 요청들은 JWT인증을 거치지 않고 이루어짐으로 webSecurityCustomizer를 통하여 각 API의 주소를 제외하도록 추가하였습니다.
사용자 인증 서비스 구현
회원가입
// AccountService.class
@Override
@Transactional
public JwtTokenResBody signUp(SignUpReqBody reqBody) {
Member member = Member.of(reqBody.getNickname(), reqBody.getEmail(), passwordEncoder.encode(reqBody.getPassword()));
Member newMember = memberRepository.save(member);
String accessToken = jwtTokenProvider.createJwt(newMember.getId());
String refreshToken = jwtTokenProvider.createRefreshJwt(accessToken);
return JwtTokenResBody.builder()
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();
}
// JwtTokenProvider.class
public String createJwt(Long memberId) {
Instant issuedDt = Instant.now().truncatedTo(ChronoUnit.SECONDS);
Instant expireDt = issuedDt.plus(accessTokenExpireSecond, ChronoUnit.SECONDS);
return Jwts.builder()
.setSubject(memberId.toString())
.setIssuedAt(Date.from(issuedDt))
.setExpiration(Date.from(expireDt))
.signWith(key)
.compact();
}
public String createRefreshJwt(String token) {
Claims claims = getClaims(token);
Instant issuedDt = Instant.now().truncatedTo(ChronoUnit.SECONDS);
Instant expireDt = issuedDt.plus(refreshTokenExpireSecond, ChronoUnit.SECONDS);
return Jwts.builder()
.setSubject(claims.getSubject())
.setIssuedAt(Date.from(issuedDt))
.setExpiration(Date.from(expireDt))
.signWith(key)
.compact();
}
회원가입 요청 시 사용자의 정보를 받아 DB에 저장 후 해당 데이터를 통하여 JWT를 발급받을 수 있도록 서비스를 생성하였습니다.
저장한 데이터의 고윳 값을 기준으로 토큰 생성 시에 JWT에 필요한 정보들을 넣어 각 만료 시간을 다르게 설정해 준 뒤 AccessToken과 RefreshToken을 생성하는 메소드를 통해 토큰을 발급받았습니다.
Swagger를 통한 사용자 인증

@GetMapping
public ResponseEntity<Object> getMe(Authentication authentication) {
MemberDetails memberDetails = (MemberDetails) authentication.getPrincipal();
return ResponseEntity.ok(memberDetails);
}
회원가입, 로그인 로직을 구현한 다음 Swagger에서 로그인 할 경우 각 토큰이 정상적으로 발급되는것을 확인 할 수 있습니다.
이전 글에 작성했던 Swagger JWT 설정을 참고하여 발급된 토큰을 넣고, 사용자를 조회하는 API를 생성한다면, Authentication 객체를 통해 해당 유저의 정보를 조회 할 수 있습니다.
사용자 인증 토큰 재발급
//AccountService.class
@Override
@Transactional
public JwtTokenResBody checkRefreshToken(RefreshTokenReqBody reqBody){
if (jwtTokenProvider.validateToken(reqBody.getRefreshToken())) {
Claims claims = jwtTokenProvider.getClaims(reqBody.getRefreshToken());
String accessToken = jwtTokenProvider.createJwt(Long.parseLong(claims.getSubject()));
return JwtTokenResBody.builder()
.accessToken(accessToken)
.refreshToken(reqBody.getRefreshToken())
.build();
}else{
throw new NotFoundException("토큰 정보가 만기 되었습니다. 재로그인 해주세요.");
}
}
//JwtTokenProvider.class
//토큰 유효성 검사
public boolean validateToken(String token) throws BadRequestException {
try {
Jws<Claims> claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
Date expireDt = claims.getBody().getExpiration();
Date nowDt = new Date();
if (expireDt.after(nowDt)) {
return true;
}
return false;
} catch (Exception e) {
throw new BadRequestException("토큰이 유효하지 않습니다.");
}
}
//토큰의 Claims 정보 추출
public Claims getClaims(String token) {
return Jwts.parserBuilder().setSigningKey(key).build()
.parseClaimsJws(token).getBody();
}
사용자가 발급된 토큰이 만료되면 로그아웃되어 다시 로그인 절차를 밟아야 합니다. 이때 RefreshToken을 통하여 토큰을 재발급 할 수 있도록 하며 로그인, 회원가입시 각각 다른 유효기간을 가진 토큰을 발급됩니다. 이를 통해 만료된 AccessToken을 RefreshToken을 통하여 재발급 하여 토큰을 교체해 줍니다.
이렇게 SpringBoot JWT 로그인, 회원가입, 토큰 재발급 예제를 구현해봤습니다. 이전 글들을 보면서 따라 해본다면 쉽게 구현할 수 있도록 작성하였습니다. 해당 코드는 저의 github에서 볼 수 있으니 참고 부탁드립니다.
'Spring' 카테고리의 다른 글
| [JPA] 영속성 컨텍스트의 특징과 프록시 이해하기 (1) | 2024.02.01 |
|---|---|
| [JPA] JPA 개념과 기본 설정 가이드 (0) | 2024.01.29 |
| [Spring] SpringBoot Swagger 연동 (3.x ver) (0) | 2024.01.10 |
| [Spring] SpringBoot Sequrity + JWT 구현 (3.x ver) (0) | 2024.01.06 |
| [Spring] Spring Security는 무엇일까? (0) | 2024.01.04 |