Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] JWT Token 인증 방식 변경 #23

Merged
merged 3 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -71,10 +72,8 @@ public ApiResponse<String> 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();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.nice.petudio.api.controller.auth.dto.request;

public record ReissueRequest(String accessToken, String refreshToken) {
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,6 +31,14 @@ protected ApiResponse<Object> handleValidateException(ValidationException except
return ApiResponse.error(exception.getErrorCode());
}

// 중복되는 데이터 저장 요청의 경우(e.g. 같은 같은 계정으로 OAuth2 회원가입 시도) 발생
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConflictException.class)
protected ApiResponse<Object> handleConflictException(ConflictException exception) {
log.error(exception.getMessage(), exception);
return ApiResponse.error(exception.getErrorCode());
}

// @Valid or @Validated 바인딩 에러시 발생
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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<MemberRole> requiredRoles) {
String jwtAccessToken = getJwtAccessTokenFromHttpCookie(request);
Expand All @@ -37,20 +35,14 @@ public Long validateAuthority(HttpServletRequest request, List<MemberRole> requi
}

private String getJwtAccessTokenFromHttpCookie(HttpServletRequest request) {
Optional<Cookie> 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<Cookie> 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<MemberRole> requiredRoles) {
if (jwtUtils.validateToken(jwtAccessToken)) {
Optional<Long> memberId = jwtUtils.parseMemberId(jwtAccessToken);
Expand Down