diff --git a/src/main/java/com/nice/petudio/api/controller/auth/AuthController.java b/src/main/java/com/nice/petudio/api/controller/auth/AuthController.java index c674f50..48fcb30 100644 --- a/src/main/java/com/nice/petudio/api/controller/auth/AuthController.java +++ b/src/main/java/com/nice/petudio/api/controller/auth/AuthController.java @@ -1,6 +1,7 @@ package com.nice.petudio.api.controller.auth; import com.nice.petudio.api.controller.auth.dto.request.LoginRequest; +import com.nice.petudio.api.controller.auth.dto.request.ReissueRequest; import com.nice.petudio.api.controller.auth.dto.request.SignUpRequest; import com.nice.petudio.api.controller.auth.vo.TokenVO; import com.nice.petudio.api.controller.auth.service.AuthService; @@ -71,10 +72,8 @@ public ApiResponse logout(@MemberId final Long memberId) { @Operation(summary = "JWT 토큰 갱신") @ResponseStatus(HttpStatus.OK) @PostMapping("/auth/reissue") - public ApiResponse reissue(@CookieValue final String accessToken, @CookieValue final String refreshToken, - HttpServletResponse response) { - TokenVO tokenVO = TokenVO.of(accessToken, refreshToken); - addTokensToCookie(createTokenService.reissueToken(tokenVO), response); + public ApiResponse reissue(ReissueRequest request, HttpServletResponse response) { + addTokensToCookie(createTokenService.reissueToken(request), response); return ApiResponse.success(); } diff --git a/src/main/java/com/nice/petudio/api/controller/auth/dto/request/ReissueRequest.java b/src/main/java/com/nice/petudio/api/controller/auth/dto/request/ReissueRequest.java new file mode 100644 index 0000000..0b70f27 --- /dev/null +++ b/src/main/java/com/nice/petudio/api/controller/auth/dto/request/ReissueRequest.java @@ -0,0 +1,4 @@ +package com.nice.petudio.api.controller.auth.dto.request; + +public record ReissueRequest(String accessToken, String refreshToken) { +} diff --git a/src/main/java/com/nice/petudio/api/controller/auth/service/CreateTokenService.java b/src/main/java/com/nice/petudio/api/controller/auth/service/CreateTokenService.java index 8bfa191..30c3fe7 100644 --- a/src/main/java/com/nice/petudio/api/controller/auth/service/CreateTokenService.java +++ b/src/main/java/com/nice/petudio/api/controller/auth/service/CreateTokenService.java @@ -1,14 +1,15 @@ package com.nice.petudio.api.controller.auth.service; +import com.nice.petudio.api.controller.auth.dto.request.ReissueRequest; import com.nice.petudio.api.controller.auth.vo.TokenVO; import com.nice.petudio.api.controller.member.service.MemberServiceUtils; -import com.nice.petudio.domain.member.Member; -import com.nice.petudio.domain.member.repository.MemberRepository; import com.nice.petudio.common.auth.jwt.JwtUtils; import com.nice.petudio.common.config.redis.constant.RedisKey; -import com.nice.petudio.common.exception.model.UnAuthorizedException; import com.nice.petudio.common.exception.error.ErrorCode; +import com.nice.petudio.common.exception.model.UnAuthorizedException; +import com.nice.petudio.domain.member.Member; +import com.nice.petudio.domain.member.repository.MemberRepository; import java.util.List; import java.util.Objects; import lombok.RequiredArgsConstructor; @@ -32,21 +33,22 @@ public TokenVO createTokenInfo(Long memberId) { return TokenVO.of(tokens.get(0), tokens.get(1)); } - public TokenVO reissueToken(final TokenVO tokenVO) { - Long memberId = jwtUtils.parseMemberId(tokenVO.getAccessToken()) + public TokenVO reissueToken(final ReissueRequest request) { + Long memberId = jwtUtils.parseMemberId(request.accessToken()) .orElseThrow(); Member member = MemberServiceUtils.findMemberById(memberRepository, memberId); - if (!jwtUtils.validateToken(tokenVO.getRefreshToken())) { + if (!jwtUtils.validateToken(request.refreshToken())) { throw new UnAuthorizedException(ErrorCode.UNAUTHORIZED_JWT_EXCEPTION, - String.format("MemberId(%d)의 토큰 갱신 요청에 포함된 Refresh Token이 유효하지 않아, Token Refresh가 수행되지 않았습니다.", memberId)); + String.format("MemberId(%d)의 토큰 갱신 요청에 포함된 Refresh Token이 유효하지 않아, Token Refresh가 수행되지 않았습니다.", + memberId)); } String refreshToken = (String) redisTemplate.opsForValue().get(RedisKey.REFRESH_TOKEN + memberId.toString()); if (Objects.isNull(refreshToken)) { throw new UnAuthorizedException(ErrorCode.UNAUTHORIZED_JWT_EXCEPTION, String.format("보관 중인 MemberId(%d)의 Refresh Token이 존재하지 않아, Token Refresh가 수행되지 않았습니다.", memberId)); } - if (!refreshToken.equals(tokenVO.getRefreshToken())) { + if (!refreshToken.equals(request.refreshToken())) { jwtUtils.expireRefreshToken(member.getId()); throw new UnAuthorizedException(ErrorCode.UNAUTHORIZED_JWT_EXCEPTION, String.format("보관 중인 MemberId(%d)의 Refresh Token이 유효하지 않아, Token Refresh가 수행되지 않았습니다.", memberId)); diff --git a/src/main/java/com/nice/petudio/common/aop/advice/ExceptionControllerAdvice.java b/src/main/java/com/nice/petudio/common/aop/advice/ExceptionControllerAdvice.java index 09e3f16..fe80d6a 100644 --- a/src/main/java/com/nice/petudio/common/aop/advice/ExceptionControllerAdvice.java +++ b/src/main/java/com/nice/petudio/common/aop/advice/ExceptionControllerAdvice.java @@ -4,6 +4,7 @@ import com.nice.petudio.api.dto.ApiResponse; import com.nice.petudio.common.exception.error.ErrorCode; import com.nice.petudio.common.exception.model.BadGatewayException; +import com.nice.petudio.common.exception.model.ConflictException; import com.nice.petudio.common.exception.model.InternalServerException; import com.nice.petudio.common.exception.model.ValidationException; import lombok.extern.slf4j.Slf4j; @@ -30,6 +31,14 @@ protected ApiResponse handleValidateException(ValidationException except return ApiResponse.error(exception.getErrorCode()); } + // 중복되는 데이터 저장 요청의 경우(e.g. 같은 같은 계정으로 OAuth2 회원가입 시도) 발생 + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(ConflictException.class) + protected ApiResponse handleConflictException(ConflictException exception) { + log.error(exception.getMessage(), exception); + return ApiResponse.error(exception.getErrorCode()); + } + // @Valid or @Validated 바인딩 에러시 발생 @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(MethodArgumentNotValidException.class) diff --git a/src/main/java/com/nice/petudio/common/auth/handler/AuthCheckHandler.java b/src/main/java/com/nice/petudio/common/auth/handler/AuthCheckHandler.java index 4095d86..12461a1 100644 --- a/src/main/java/com/nice/petudio/common/auth/handler/AuthCheckHandler.java +++ b/src/main/java/com/nice/petudio/common/auth/handler/AuthCheckHandler.java @@ -1,21 +1,20 @@ package com.nice.petudio.common.auth.handler; import com.nice.petudio.api.controller.member.service.MemberServiceUtils; -import com.nice.petudio.domain.member.Member; -import com.nice.petudio.domain.member.MemberRole; -import com.nice.petudio.domain.member.repository.MemberRepository; import com.nice.petudio.common.auth.jwt.JwtUtils; +import com.nice.petudio.common.exception.error.ErrorCode; import com.nice.petudio.common.exception.model.ForbiddenException; import com.nice.petudio.common.exception.model.UnAuthorizedException; import com.nice.petudio.common.exception.model.ValidationException; -import com.nice.petudio.common.exception.error.ErrorCode; -import jakarta.servlet.http.Cookie; +import com.nice.petudio.domain.member.Member; +import com.nice.petudio.domain.member.MemberRole; +import com.nice.petudio.domain.member.repository.MemberRepository; import jakarta.servlet.http.HttpServletRequest; -import java.util.Arrays; import java.util.List; import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; @RequiredArgsConstructor @Component @@ -25,7 +24,6 @@ public class AuthCheckHandler { private final MemberRepository memberRepository; private Long memberId; - private static final String JWT_ACCESS_TOKEN_COOKIE_NAME = "accessToken"; public Long validateAuthority(HttpServletRequest request, List requiredRoles) { String jwtAccessToken = getJwtAccessTokenFromHttpCookie(request); @@ -37,20 +35,14 @@ public Long validateAuthority(HttpServletRequest request, List requi } private String getJwtAccessTokenFromHttpCookie(HttpServletRequest request) { - Optional jwtAccessTokenCookie = getJwtAccessTokenCookieFromHttpRequest(request); - if (jwtAccessTokenCookie.isPresent()) { - return jwtAccessTokenCookie.get().getValue(); + String bearerToken = request.getHeader("Authorization"); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring("Bearer ".length()); } throw new ValidationException(ErrorCode.INVALID_JWT_TOKEN_EXCEPTION, ErrorCode.INVALID_JWT_TOKEN_EXCEPTION.getMessage()); } - private Optional getJwtAccessTokenCookieFromHttpRequest(HttpServletRequest request) { - return Arrays.stream(request.getCookies()) - .filter(cookie -> cookie.getName().equals(JWT_ACCESS_TOKEN_COOKIE_NAME)) - .findFirst(); - } - public boolean hasAuthority(String jwtAccessToken, List requiredRoles) { if (jwtUtils.validateToken(jwtAccessToken)) { Optional memberId = jwtUtils.parseMemberId(jwtAccessToken);