Skip to content

Commit

Permalink
Merge pull request #232 from TEAM-MODDY/feat/#210
Browse files Browse the repository at this point in the history
#210 [feat] 토큰 갱신 api 구현
  • Loading branch information
KWY0218 authored Feb 5, 2024
2 parents aa6bcc7 + c3348a8 commit aa00384
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public enum SuccessCode {
FIND_REGION_LIST_SUCCESS(HttpStatus.OK, "희망 지역 리스트 조회 성공입니다."),
VERIFICATION_CODE_MATCH_SUCCESS(HttpStatus.OK, "전화번호 인증 성공입니다."),
LOGOUT_SUCCESS(HttpStatus.OK, "로그아웃 성공입니다."),
REFRESH_SUCCESS(HttpStatus.OK, "토큰 갱신 성공입니다."),
CREATE_MODEL_APPLICATION_SUCCESS(HttpStatus.OK, "모델 지원서 생성 성공입니다."),
USER_WITHDRAW_SUCCESS(HttpStatus.OK, "회원 탈퇴 성공입니다."),
GET_PRE_SIGNED_URL_SUCCESS(HttpStatus.OK, "제안서 다운로드 url 생성 성공");
Expand Down
32 changes: 25 additions & 7 deletions src/main/java/com/moddy/server/config/jwt/JwtService.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,32 @@
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.moddy.server.common.exception.enums.ErrorCode.TOKEN_TIME_EXPIRED_EXCEPTION;

@Service
@RequiredArgsConstructor
public class JwtService {
@Value("${jwt.secret}")
private String jwtSecret;
private static final String USER_ID = "USER_ID";
private static final String ACCESS_TOKEN = "ACCESS_TOKEN";
private static final String REFRESH_TOKEN = "REFRESH_TOKEN";
public static final int MINUTE_IN_MILLISECONDS = 60 * 1000;
public static final long DAYS_IN_MILLISECONDS = 24 * 60 * 60 * 1000L;
public static final int ACCESS_TOKEN_EXPIRATION_DAYS = 30;
public static final int REFRESH_TOKEN_EXPIRATION_DAYS = 60;
public static final int ACCESS_TOKEN_EXPIRATION_MINUTE = 10;
public static final int REFRESH_TOKEN_EXPIRATION_DAYS = 14;
private final RedisTemplate<String, String> redisTemplate;

@PostConstruct
protected void init() {
Expand Down Expand Up @@ -69,11 +73,22 @@ public String getUserIdInToken(final String token) {
}

public TokenPair generateTokenPair(final String userId) {
String accessToken = createAccessToken(userId);
String refreshToken = createRefreshToken(userId);
final String accessToken = createAccessToken(userId);
final String refreshToken = createRefreshToken(userId);
saveRefreshToken(userId, refreshToken);
return new TokenPair(accessToken, refreshToken);
}

public boolean compareRefreshToken(final String userId, final String refreshToken) {
final String storedRefreshToken = redisTemplate.opsForValue().get(userId);
if (storedRefreshToken == null) return false;
return storedRefreshToken.equals(refreshToken);
}

public void saveRefreshToken(final String userId, final String refreshToken) {
redisTemplate.opsForValue().set(userId, refreshToken, REFRESH_TOKEN_EXPIRATION_DAYS, TimeUnit.DAYS);
}

private String createToken(final Claims claims) {
return Jwts.builder()
.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
Expand All @@ -95,7 +110,7 @@ private Claims getAccessTokenClaims() {
return Jwts.claims()
.setSubject(ACCESS_TOKEN)
.setIssuedAt(now)
.setExpiration(new Date(now.getTime() + ACCESS_TOKEN_EXPIRATION_DAYS * DAYS_IN_MILLISECONDS));
.setExpiration(new Date(now.getTime() + ACCESS_TOKEN_EXPIRATION_MINUTE * MINUTE_IN_MILLISECONDS));
}

private Claims getBody(final String token) {
Expand All @@ -111,4 +126,7 @@ private Key getSigningKey() {
return Keys.hmacShaKeyFor(keyBytes);
}

public void deleteRefreshToken(final String userId) {
redisTemplate.delete(userId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m
final HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
final String token = request.getHeader("Authorization");
if (token == null || token.isBlank() || !token.startsWith("Bearer ")) {
throw new UnAuthorizedException(TOKEN_NOT_CONTAINED_EXCEPTION);
throw new BadRequestException(TOKEN_NOT_CONTAINED_EXCEPTION);
}
final String encodedUserId = token.substring("Bearer ".length());
if (!jwtService.verifyToken(encodedUserId)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import com.moddy.server.common.dto.ErrorResponse;
import com.moddy.server.common.dto.SuccessNonDataResponse;
import com.moddy.server.common.dto.SuccessResponse;
import com.moddy.server.common.dto.TokenPair;
import com.moddy.server.common.exception.enums.SuccessCode;
import com.moddy.server.config.resolver.kakao.KakaoCode;
import com.moddy.server.config.resolver.user.UserId;
import com.moddy.server.controller.auth.dto.request.PhoneNumberRequestDto;
import com.moddy.server.controller.auth.dto.request.TokenRequestDto;
import com.moddy.server.controller.auth.dto.request.VerifyCodeRequestDto;
import com.moddy.server.controller.auth.dto.response.LoginResponseDto;
import com.moddy.server.controller.designer.dto.request.DesignerCreateRequest;
Expand Down Expand Up @@ -36,6 +38,7 @@
import java.io.IOException;

import static com.moddy.server.common.exception.enums.SuccessCode.LOGOUT_SUCCESS;
import static com.moddy.server.common.exception.enums.SuccessCode.REFRESH_SUCCESS;
import static com.moddy.server.common.exception.enums.SuccessCode.SEND_VERIFICATION_CODE_SUCCESS;
import static com.moddy.server.common.exception.enums.SuccessCode.SOCIAL_LOGIN_SUCCESS;
import static com.moddy.server.common.exception.enums.SuccessCode.VERIFICATION_CODE_MATCH_SUCCESS;
Expand Down Expand Up @@ -118,9 +121,21 @@ public SuccessNonDataResponse verifyCode(@Valid @RequestBody VerifyCodeRequestDt
})
@SecurityRequirement(name = "JWT Auth")
@PostMapping("/logout")
public SuccessNonDataResponse logout(@UserId Long userId) {
public SuccessNonDataResponse logout(@Parameter(hidden = true) @UserId Long userId) {
authService.logout(userId);
return SuccessNonDataResponse.success(LOGOUT_SUCCESS);
}

@Operation(summary = "토큰 갱신 API", description = "토큰 갱신 API")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "토큰 갱신 성공입니다."),
@ApiResponse(responseCode = "401", description = "토큰이만료되었습니다. 다시 로그인해주세요.", content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode = "404", description = "존재하지 않는 유저입니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode = "500", description = "서버 내부 오류", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
})
@SecurityRequirement(name = "JWT Auth")
@PostMapping("/refresh")
public SuccessResponse<TokenPair> refresh(@RequestBody final TokenRequestDto tokenRequestDto) {
return SuccessResponse.success(REFRESH_SUCCESS, authService.refresh(tokenRequestDto));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.moddy.server.controller.auth.dto.request;

public record TokenRequestDto(String accessToken, String refreshToken) {
}
22 changes: 20 additions & 2 deletions src/main/java/com/moddy/server/service/auth/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import com.moddy.server.common.exception.model.BadRequestException;
import com.moddy.server.common.exception.model.NotFoundException;
import com.moddy.server.common.exception.model.NotFoundUserException;
import com.moddy.server.common.exception.model.UnAuthorizedException;
import com.moddy.server.common.util.SmsUtil;
import com.moddy.server.common.util.VerificationCodeGenerator;
import com.moddy.server.config.jwt.JwtService;
import com.moddy.server.controller.auth.dto.request.TokenRequestDto;
import com.moddy.server.controller.auth.dto.response.LoginResponseDto;
import com.moddy.server.controller.designer.dto.response.UserCreateResponse;
import com.moddy.server.domain.user.User;
Expand All @@ -22,6 +24,7 @@
import static com.moddy.server.common.exception.enums.ErrorCode.INVALID_PHONE_NUMBER_EXCEPTION;
import static com.moddy.server.common.exception.enums.ErrorCode.NOT_FOUND_VERIFICATION_CODE_EXCEPTION;
import static com.moddy.server.common.exception.enums.ErrorCode.NOT_MATCH_VERIFICATION_CODE_EXCEPTION;
import static com.moddy.server.common.exception.enums.ErrorCode.TOKEN_TIME_EXPIRED_EXCEPTION;
import static com.moddy.server.common.exception.enums.ErrorCode.USER_NOT_FOUND_EXCEPTION;


Expand Down Expand Up @@ -81,7 +84,22 @@ public void verifyCode(final String phoneNumber, final String verificationCode)
smsUtil.deleteVerificationCode(phoneNumber);
}

public void logout(Long userId) {
User user = userRepository.findById(userId).orElseThrow(() -> new NotFoundException(USER_NOT_FOUND_EXCEPTION));
public void logout(final Long userId) {
final User user = userRepository.findById(userId).orElseThrow(() -> new NotFoundException(USER_NOT_FOUND_EXCEPTION));
jwtService.deleteRefreshToken(String.valueOf(userId));
}

public TokenPair refresh(final TokenRequestDto tokenRequestDto) {
final String userId = jwtService.getUserIdInToken(tokenRequestDto.accessToken());
final User user = userRepository.findById(Long.parseLong(userId)).orElseThrow(() -> new NotFoundException(USER_NOT_FOUND_EXCEPTION));
if (!jwtService.compareRefreshToken(userId, tokenRequestDto.refreshToken()))
throw new UnAuthorizedException(TOKEN_TIME_EXPIRED_EXCEPTION);

if (!jwtService.verifyToken(tokenRequestDto.refreshToken()))
throw new UnAuthorizedException(TOKEN_TIME_EXPIRED_EXCEPTION);

final TokenPair tokenPair = jwtService.generateTokenPair(userId);
jwtService.saveRefreshToken(userId, tokenPair.refreshToken());
return tokenPair;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.moddy.server.service.user;

import com.moddy.server.common.exception.model.NotFoundException;
import com.moddy.server.config.jwt.JwtService;
import com.moddy.server.domain.user.User;
import com.moddy.server.domain.user.repository.UserRepository;
import com.moddy.server.service.application.HairModelApplicationRegisterService;
Expand All @@ -22,12 +23,14 @@ public class UserRegisterService {
private final HairModelApplicationRegisterService hairModelApplicationRegisterService;
private final ModelRegisterService modelRegisterService;
private final DesignerRegisterService designerRegisterService;
private final JwtService jwtService;

@Transactional
public void withdraw(final Long userId) {
final User user = userRepository.findById(userId).orElseThrow(() -> new NotFoundException(USER_NOT_FOUND_EXCEPTION));
if (user.getRole() == MODEL) deleteModelInfos(userId);
else deleteDesignerInfos(user);
jwtService.deleteRefreshToken(String.valueOf(userId));
}

private void deleteModelInfos(final Long modelId) {
Expand Down

0 comments on commit aa00384

Please sign in to comment.