diff --git a/src/main/java/com/dnd/gongmuin/auth/cotroller/AuthController.java b/src/main/java/com/dnd/gongmuin/auth/cotroller/AuthController.java index 014ac7e5..fbd8ae9d 100644 --- a/src/main/java/com/dnd/gongmuin/auth/cotroller/AuthController.java +++ b/src/main/java/com/dnd/gongmuin/auth/cotroller/AuthController.java @@ -16,7 +16,7 @@ @RequestMapping("/api/auth") public class AuthController { - @GetMapping("signin/kakao") + @GetMapping("/signin/kakao") public ResponseEntity kakaoLoginRedirect() { HttpHeaders httpHeaders = new HttpHeaders(); // 카카오 로그인 페이지로 리다이렉트 diff --git a/src/main/java/com/dnd/gongmuin/auth/domain/Provider.java b/src/main/java/com/dnd/gongmuin/auth/domain/Provider.java index 97072568..5d80fe35 100644 --- a/src/main/java/com/dnd/gongmuin/auth/domain/Provider.java +++ b/src/main/java/com/dnd/gongmuin/auth/domain/Provider.java @@ -15,11 +15,18 @@ public enum Provider { KAKAO("kakao"), NAVER("naver"); - private final String provider; + private final String label; public static Provider fromProviderName(String providerName) { return Arrays.stream(values()) - .filter(provider -> provider.getProvider().equalsIgnoreCase(providerName)) + .filter(provider -> provider.getLabel().equalsIgnoreCase(providerName)) + .findFirst() + .orElseThrow(() -> new NotFoundException(AuthErrorCode.NOT_FOUND_PROVIDER)); + } + + public static Provider fromSocialEmail(String socialEmail) { + return Arrays.stream(values()) + .filter(provider -> socialEmail.contains(provider.getLabel())) .findFirst() .orElseThrow(() -> new NotFoundException(AuthErrorCode.NOT_FOUND_PROVIDER)); } diff --git a/src/main/java/com/dnd/gongmuin/auth/exception/AuthErrorCode.java b/src/main/java/com/dnd/gongmuin/auth/exception/AuthErrorCode.java index ac3cf45b..7a96f39b 100644 --- a/src/main/java/com/dnd/gongmuin/auth/exception/AuthErrorCode.java +++ b/src/main/java/com/dnd/gongmuin/auth/exception/AuthErrorCode.java @@ -11,7 +11,8 @@ public enum AuthErrorCode implements ErrorCode { UNSUPPORTED_SOCIAL_LOGIN("해당 소셜 로그인은 지원되지 않습니다.", "AUTH_001"), NOT_FOUND_PROVIDER("알맞은 Provider를 찾을 수 없습니다.", "AUTH_002"), - NOT_FOUND_AUTH("회원의 AUTH를 찾을 수 없습니다.", "AUTH_003"); + NOT_FOUND_AUTH("회원의 AUTH를 찾을 수 없습니다.", "AUTH_003"), + UNAUTHORIZED_TOKEN("잘못된 토큰입니다.", "AUTH_004"); private final String message; private final String code; diff --git a/src/main/java/com/dnd/gongmuin/auth/service/AuthService.java b/src/main/java/com/dnd/gongmuin/auth/service/AuthService.java index 64326c73..0bae06db 100644 --- a/src/main/java/com/dnd/gongmuin/auth/service/AuthService.java +++ b/src/main/java/com/dnd/gongmuin/auth/service/AuthService.java @@ -43,8 +43,7 @@ public boolean isAuthStatusOld(Member member) { } private Auth createAuth(Member savedMember) { - String providerName = memberService.parseProviderFromSocialEmail(savedMember); - Provider provider = Provider.fromProviderName(providerName); + Provider provider = memberService.parseProviderFromSocialEmail(savedMember); return Auth.of(provider, AuthStatus.NEW, savedMember); } diff --git a/src/main/java/com/dnd/gongmuin/mail/service/MailService.java b/src/main/java/com/dnd/gongmuin/mail/service/MailService.java index 855e8987..4fab4401 100644 --- a/src/main/java/com/dnd/gongmuin/mail/service/MailService.java +++ b/src/main/java/com/dnd/gongmuin/mail/service/MailService.java @@ -7,6 +7,7 @@ import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import com.dnd.gongmuin.common.exception.runtime.NotFoundException; import com.dnd.gongmuin.mail.dto.MailMapper; @@ -16,7 +17,7 @@ import com.dnd.gongmuin.mail.dto.response.SendMailResponse; import com.dnd.gongmuin.mail.exception.MailErrorCode; import com.dnd.gongmuin.mail.util.AuthCodeGenerator; -import com.dnd.gongmuin.member.service.MemberService; +import com.dnd.gongmuin.member.repository.MemberRepository; import com.dnd.gongmuin.redis.util.RedisUtil; import jakarta.mail.internet.MimeMessage; @@ -28,13 +29,14 @@ public class MailService { @Value("${spring.mail.auth-code-expiration-millis}") private long authCodeExpirationMillis; - private final String SUBJECT = "[공무인] 공무원 인증 메일입니다."; + private static final String SUBJECT = "[공무인] 공무원 인증 메일입니다."; private static final String AUTH_CODE_PREFIX = "AuthCode "; + private static final String TEXT = "인증 코드는 다음과 같습니다.\n "; private final JavaMailSender mailSender; private final AuthCodeGenerator authCodeGenerator; private final RedisUtil redisUtil; - private final MemberService memberService; + private final MemberRepository memberRepository; public SendMailResponse sendEmail(SendMailRequest request) { String targetEmail = request.targetEmail(); @@ -71,7 +73,7 @@ private MimeMessage createMail(String targetEmail) { MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, false, "UTF-8"); messageHelper.setTo(targetEmail); messageHelper.setSubject(SUBJECT); - messageHelper.setText("인증 코드는 다음과 같습니다.\n" + authCode); + messageHelper.setText(TEXT + authCode); return mimeMessage; } catch (IllegalArgumentException e) { @@ -86,8 +88,9 @@ private void saveAuthCodeToRedis(String targetEmail, String authCode, long authC redisUtil.setValues(key, authCode, Duration.ofMillis(authCodeExpirationMillis)); } - private void checkDuplicatedOfficialEmail(String officialEmail) { - if (memberService.isOfficialEmailExists(officialEmail)) { + @Transactional(readOnly = true) + public void checkDuplicatedOfficialEmail(String officialEmail) { + if (memberRepository.existsByOfficialEmail(officialEmail)) { throw new NotFoundException(MailErrorCode.DUPLICATED_ERROR); } } diff --git a/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java b/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java index ddc7edce..18b47fea 100644 --- a/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java +++ b/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java @@ -7,13 +7,18 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.dnd.gongmuin.member.domain.Member; import com.dnd.gongmuin.member.dto.request.AdditionalInfoRequest; +import com.dnd.gongmuin.member.dto.request.LogoutRequest; +import com.dnd.gongmuin.member.dto.request.ReissueRequest; import com.dnd.gongmuin.member.dto.request.ValidateNickNameRequest; +import com.dnd.gongmuin.member.dto.response.LogoutResponse; +import com.dnd.gongmuin.member.dto.response.ReissueResponse; import com.dnd.gongmuin.member.dto.response.SignUpResponse; import com.dnd.gongmuin.member.dto.response.ValidateNickNameResponse; import com.dnd.gongmuin.member.service.MemberService; -import com.dnd.gongmuin.security.oauth2.CustomOauth2User; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @RestController @@ -25,16 +30,28 @@ public class MemberController { @PostMapping("/check-nickname") public ResponseEntity checkNickName( - @RequestBody ValidateNickNameRequest validateNickNameRequest) { + @RequestBody @Valid ValidateNickNameRequest validateNickNameRequest) { return ResponseEntity.ok(memberService.isDuplicatedNickname(validateNickNameRequest)); } @PostMapping("/member") - public ResponseEntity signUp(@RequestBody AdditionalInfoRequest request, - @AuthenticationPrincipal CustomOauth2User loginMember) { - SignUpResponse response = memberService.signUp(request, loginMember.getEmail()); + public ResponseEntity signUp( + @RequestBody @Valid AdditionalInfoRequest request, + @AuthenticationPrincipal Member loginMember) { + SignUpResponse response = memberService.signUp(request, loginMember.getSocialEmail()); return ResponseEntity.ok(response); } + @PostMapping("/logout") + public ResponseEntity logout(@RequestBody @Valid LogoutRequest request) { + LogoutResponse response = memberService.logout(request); + return ResponseEntity.ok(response); + } + + @PostMapping("/reissue/token") + public ResponseEntity reissue(@RequestBody @Valid ReissueRequest request) { + ReissueResponse response = memberService.reissue(request); + return ResponseEntity.ok(response); + } } diff --git a/src/main/java/com/dnd/gongmuin/member/dto/request/LogoutRequest.java b/src/main/java/com/dnd/gongmuin/member/dto/request/LogoutRequest.java new file mode 100644 index 00000000..dda1ac4f --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/member/dto/request/LogoutRequest.java @@ -0,0 +1,10 @@ +package com.dnd.gongmuin.member.dto.request; + +import jakarta.validation.constraints.NotEmpty; + +public record LogoutRequest( + @NotEmpty(message = "AccessToken을 입력해주세요.") + String accessToken +) { + +} diff --git a/src/main/java/com/dnd/gongmuin/member/dto/request/ReissueRequest.java b/src/main/java/com/dnd/gongmuin/member/dto/request/ReissueRequest.java new file mode 100644 index 00000000..1275d413 --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/member/dto/request/ReissueRequest.java @@ -0,0 +1,9 @@ +package com.dnd.gongmuin.member.dto.request; + +import jakarta.validation.constraints.NotEmpty; + +public record ReissueRequest( + @NotEmpty(message = "AccessToken을 입력해주세요.") + String accessToken +) { +} diff --git a/src/main/java/com/dnd/gongmuin/member/dto/response/LogoutResponse.java b/src/main/java/com/dnd/gongmuin/member/dto/response/LogoutResponse.java new file mode 100644 index 00000000..457f4481 --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/member/dto/response/LogoutResponse.java @@ -0,0 +1,6 @@ +package com.dnd.gongmuin.member.dto.response; + +public record LogoutResponse( + boolean result +) { +} diff --git a/src/main/java/com/dnd/gongmuin/member/dto/response/ReissueResponse.java b/src/main/java/com/dnd/gongmuin/member/dto/response/ReissueResponse.java new file mode 100644 index 00000000..db96897a --- /dev/null +++ b/src/main/java/com/dnd/gongmuin/member/dto/response/ReissueResponse.java @@ -0,0 +1,6 @@ +package com.dnd.gongmuin.member.dto.response; + +public record ReissueResponse( + String accessToken +) { +} diff --git a/src/main/java/com/dnd/gongmuin/member/exception/MemberErrorCode.java b/src/main/java/com/dnd/gongmuin/member/exception/MemberErrorCode.java index be9f9919..783f20d1 100644 --- a/src/main/java/com/dnd/gongmuin/member/exception/MemberErrorCode.java +++ b/src/main/java/com/dnd/gongmuin/member/exception/MemberErrorCode.java @@ -11,7 +11,8 @@ public enum MemberErrorCode implements ErrorCode { NOT_FOUND_MEMBER("특정 회원을 찾을 수 없습니다.", "MEMBER_001"), NOT_FOUND_NEW_MEMBER("신규 회원이 아닙니다.", "MEMBER_002"), - NOT_ENOUGH_CREDIT("보유한 크레딧이 부족합니다.", "MEMBER_003"); + LOGOUT_FAILED("로그아웃을 실패했습니다.", "MEMBER_003"), + NOT_ENOUGH_CREDIT("보유한 크레딧이 부족합니다.", "MEMBER_004"); private final String message; private final String code; diff --git a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java index 78184133..1e995f9c 100644 --- a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java +++ b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java @@ -1,21 +1,34 @@ package com.dnd.gongmuin.member.service; +import java.time.Duration; +import java.util.Date; import java.util.Objects; +import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import com.dnd.gongmuin.auth.domain.Provider; import com.dnd.gongmuin.auth.exception.AuthErrorCode; import com.dnd.gongmuin.common.exception.runtime.NotFoundException; +import com.dnd.gongmuin.common.exception.runtime.ValidationException; import com.dnd.gongmuin.member.domain.JobCategory; import com.dnd.gongmuin.member.domain.JobGroup; import com.dnd.gongmuin.member.domain.Member; import com.dnd.gongmuin.member.dto.request.AdditionalInfoRequest; +import com.dnd.gongmuin.member.dto.request.LogoutRequest; +import com.dnd.gongmuin.member.dto.request.ReissueRequest; import com.dnd.gongmuin.member.dto.request.ValidateNickNameRequest; +import com.dnd.gongmuin.member.dto.response.LogoutResponse; +import com.dnd.gongmuin.member.dto.response.ReissueResponse; import com.dnd.gongmuin.member.dto.response.SignUpResponse; import com.dnd.gongmuin.member.dto.response.ValidateNickNameResponse; import com.dnd.gongmuin.member.exception.MemberErrorCode; import com.dnd.gongmuin.member.repository.MemberRepository; +import com.dnd.gongmuin.redis.util.RedisUtil; +import com.dnd.gongmuin.security.jwt.util.TokenProvider; +import com.dnd.gongmuin.security.oauth2.AuthInfo; +import com.dnd.gongmuin.security.oauth2.CustomOauth2User; import com.dnd.gongmuin.security.oauth2.Oauth2Response; import lombok.RequiredArgsConstructor; @@ -24,7 +37,11 @@ @RequiredArgsConstructor public class MemberService { + private static final String TOKEN_PREFIX = "Bearer "; + private static final String LOGOUT = "logout"; private final MemberRepository memberRepository; + private final TokenProvider tokenProvider; + private final RedisUtil redisUtil; public Member saveOrUpdate(Oauth2Response oauth2Response) { Member member = memberRepository.findBySocialEmail(oauth2Response.createSocialEmail()) @@ -37,14 +54,9 @@ public Member saveOrUpdate(Oauth2Response oauth2Response) { return memberRepository.save(member); } - public String parseProviderFromSocialEmail(Member member) { - String socialEmail = member.getSocialEmail().toUpperCase(); - if (socialEmail.contains("KAKAO")) { - return "KAKAO"; - } else if (socialEmail.contains("NAVER")) { - return "NAVER"; - } - throw new NotFoundException(AuthErrorCode.NOT_FOUND_PROVIDER); + public Provider parseProviderFromSocialEmail(Member member) { + String socialEmail = member.getSocialEmail(); + return Provider.fromSocialEmail(socialEmail); } private Member createMemberFromOauth2Response(Oauth2Response oauth2Response) { @@ -57,23 +69,23 @@ public boolean isOfficialEmail(Member member) { @Transactional(readOnly = true) public ValidateNickNameResponse isDuplicatedNickname(ValidateNickNameRequest request) { - boolean isDuplicate = memberRepository.existsByNickname(request.nickname()); + boolean isDuplicated = memberRepository.existsByNickname(request.nickname()); - return new ValidateNickNameResponse(isDuplicate); + return new ValidateNickNameResponse(isDuplicated); } @Transactional public SignUpResponse signUp(AdditionalInfoRequest request, String email) { - Member findMember = memberRepository.findBySocialEmail(email) + Member foundMember = memberRepository.findBySocialEmail(email) .orElseThrow(() -> new NotFoundException(MemberErrorCode.NOT_FOUND_MEMBER)); - if (!isOfficialEmail(findMember)) { - new NotFoundException(MemberErrorCode.NOT_FOUND_NEW_MEMBER); + if (!isOfficialEmail(foundMember)) { + throw new NotFoundException(MemberErrorCode.NOT_FOUND_NEW_MEMBER); } - Member signUpMember = updateAdditionalInfo(request, findMember); + updateAdditionalInfo(request, foundMember); - return new SignUpResponse(signUpMember.getNickname()); + return new SignUpResponse(foundMember.getNickname()); } public Member getMemberBySocialEmail(String socialEmail) { @@ -81,21 +93,72 @@ public Member getMemberBySocialEmail(String socialEmail) { .orElseThrow(() -> new NotFoundException(MemberErrorCode.NOT_FOUND_MEMBER)); } - private Member updateAdditionalInfo(AdditionalInfoRequest request, Member findMember) { + private void updateAdditionalInfo(AdditionalInfoRequest request, Member findMember) { findMember.updateAdditionalInfo( request.nickname(), request.officialEmail(), JobGroup.of(request.jobGroup()), JobCategory.of(request.jobCategory()) ); - - return memberRepository.save(findMember); } @Transactional(readOnly = true) public boolean isOfficialEmailExists(String officialEmail) { - boolean result = memberRepository.existsByOfficialEmail(officialEmail); + return memberRepository.existsByOfficialEmail(officialEmail); + } + + public LogoutResponse logout(LogoutRequest request) { + String accessToken = request.accessToken().substring(TOKEN_PREFIX.length()); + + if (!tokenProvider.validateToken(accessToken, new Date())) { + throw new ValidationException(AuthErrorCode.UNAUTHORIZED_TOKEN); + } + + Authentication authentication = tokenProvider.getAuthentication(accessToken); + Member member = (Member)authentication.getPrincipal(); + + if (!Objects.isNull(redisUtil.getValues("RT:" + member.getSocialEmail()))) { + redisUtil.deleteValues("RT:" + member.getSocialEmail()); + } + + Long expiration = tokenProvider.getExpiration(accessToken, new Date()); + redisUtil.setValues(accessToken, LOGOUT, Duration.ofMillis(expiration)); + + String values = redisUtil.getValues(accessToken); + if (!Objects.equals(values, LOGOUT)) { + throw new NotFoundException(MemberErrorCode.LOGOUT_FAILED); + } + + return new LogoutResponse(true); + } + + public ReissueResponse reissue(ReissueRequest request) { + String accessToken = request.accessToken().substring(TOKEN_PREFIX.length()); + + if (!tokenProvider.validateToken(accessToken, new Date())) { + throw new ValidationException(AuthErrorCode.UNAUTHORIZED_TOKEN); + } + + // 로그아웃 토큰 처리 + if ("logout".equals(redisUtil.getValues(accessToken))) { + throw new ValidationException(AuthErrorCode.UNAUTHORIZED_TOKEN); + } + + Authentication authentication = tokenProvider.getAuthentication(accessToken); + Member member = (Member)authentication.getPrincipal(); + + String refreshToken = redisUtil.getValues("RT:" + member.getSocialEmail()); + + // 로그아웃 또는 토큰 만료 경우 처리 + if ("false".equals(refreshToken)) { + throw new ValidationException(AuthErrorCode.UNAUTHORIZED_TOKEN); + } + + CustomOauth2User customUser = new CustomOauth2User( + AuthInfo.of(member.getSocialName(), member.getSocialEmail())); + String reissuedAccessToken = tokenProvider.generateAccessToken(customUser, new Date()); + tokenProvider.generateRefreshToken(customUser, new Date()); - return result; + return new ReissueResponse(reissuedAccessToken); } } \ No newline at end of file diff --git a/src/main/java/com/dnd/gongmuin/security/handler/CustomOauth2SuccessHandler.java b/src/main/java/com/dnd/gongmuin/security/handler/CustomOauth2SuccessHandler.java index dbafd2e9..a9f92f10 100644 --- a/src/main/java/com/dnd/gongmuin/security/handler/CustomOauth2SuccessHandler.java +++ b/src/main/java/com/dnd/gongmuin/security/handler/CustomOauth2SuccessHandler.java @@ -39,6 +39,8 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo .orElseThrow(() -> new NotFoundException(MemberErrorCode.NOT_FOUND_MEMBER)); String token = tokenProvider.generateAccessToken(customOauth2User, new Date()); + tokenProvider.generateRefreshToken(customOauth2User, new Date()); + response.setHeader("Authorization", token); if (!isAuthStatusOld(findmember)) { diff --git a/src/main/java/com/dnd/gongmuin/security/jwt/util/TokenAuthenticationFilter.java b/src/main/java/com/dnd/gongmuin/security/jwt/util/TokenAuthenticationFilter.java index 5e8e126b..3de5e805 100644 --- a/src/main/java/com/dnd/gongmuin/security/jwt/util/TokenAuthenticationFilter.java +++ b/src/main/java/com/dnd/gongmuin/security/jwt/util/TokenAuthenticationFilter.java @@ -11,6 +11,8 @@ import org.springframework.util.ObjectUtils; import org.springframework.web.filter.OncePerRequestFilter; +import com.dnd.gongmuin.redis.util.RedisUtil; + import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -23,6 +25,7 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { private static final String TOKEN_PREFIX = "Bearer "; private final TokenProvider tokenProvider; + private final RedisUtil redisUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, @@ -31,9 +34,11 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse String accessToken = resolveToken(request); if (tokenProvider.validateToken(accessToken, new Date())) { - saveAuthentication(accessToken); - } else { - // TODO : 만료시 accessToken 재발급 + // accessToken logout 여부 확인 + String isLogout = redisUtil.getValues(accessToken); + if ("false".equals(isLogout)) { + saveAuthentication(accessToken); + } } filterChain.doFilter(request, response); diff --git a/src/main/java/com/dnd/gongmuin/security/jwt/util/TokenProvider.java b/src/main/java/com/dnd/gongmuin/security/jwt/util/TokenProvider.java index 899d0a15..5a1aa531 100644 --- a/src/main/java/com/dnd/gongmuin/security/jwt/util/TokenProvider.java +++ b/src/main/java/com/dnd/gongmuin/security/jwt/util/TokenProvider.java @@ -1,5 +1,6 @@ package com.dnd.gongmuin.security.jwt.util; +import java.time.Duration; import java.util.Collections; import java.util.Date; import java.util.List; @@ -17,11 +18,13 @@ import org.springframework.util.StringUtils; import com.dnd.gongmuin.common.exception.runtime.CustomJwtException; +import com.dnd.gongmuin.common.exception.runtime.NotFoundException; import com.dnd.gongmuin.member.domain.Member; -import com.dnd.gongmuin.member.service.MemberService; +import com.dnd.gongmuin.member.exception.MemberErrorCode; +import com.dnd.gongmuin.member.repository.MemberRepository; +import com.dnd.gongmuin.redis.util.RedisUtil; import com.dnd.gongmuin.security.jwt.exception.JwtErrorCode; import com.dnd.gongmuin.security.oauth2.CustomOauth2User; -import com.dnd.gongmuin.security.service.TokenService; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; @@ -38,8 +41,8 @@ public class TokenProvider { private static final String ROLE_KEY = "ROLE"; private static final long ACCESS_TOKEN_EXPIRE_TIME = 1000 * 60 * 30L; private static final long REFRESH_TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 24L; - private final TokenService tokenService; - private final MemberService memberService; + private final MemberRepository memberRepository; + private final RedisUtil redisUtil; @Value("${spring.jwt.key}") private String key; private SecretKey secretKey; @@ -55,7 +58,10 @@ public String generateAccessToken(CustomOauth2User authentication, Date now) { public String generateRefreshToken(CustomOauth2User authentication, Date now) { String refreshToken = generateToken(authentication, REFRESH_TOKEN_EXPIRE_TIME, now); - // TODO : RedisRepo refreshToken SAVE + + // redis Refresh 저장 + redisUtil.setValues("RT:" + authentication.getEmail(), refreshToken, + Duration.ofMillis(REFRESH_TOKEN_EXPIRE_TIME)); return refreshToken; } @@ -87,7 +93,8 @@ public Authentication getAuthentication(String token) { List authorities = getAuthorities(claims); String socialEmail = claims.getSubject(); - Member principal = memberService.getMemberBySocialEmail(socialEmail); + Member principal = memberRepository.findBySocialEmail(socialEmail) + .orElseThrow(() -> new NotFoundException(MemberErrorCode.NOT_FOUND_MEMBER)); return new UsernamePasswordAuthenticationToken(principal, token, authorities); } @@ -122,4 +129,10 @@ private List getAuthorities(Claims claims) { )); } + public Long getExpiration(String token, Date date) { + Claims claims = parseToken(token); + Date expiration = claims.getExpiration(); + return (expiration.getTime() - date.getTime()); + } + } diff --git a/src/main/java/com/dnd/gongmuin/security/service/TokenService.java b/src/main/java/com/dnd/gongmuin/security/service/TokenService.java deleted file mode 100644 index 4f73227d..00000000 --- a/src/main/java/com/dnd/gongmuin/security/service/TokenService.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.dnd.gongmuin.security.service; - -import org.springframework.stereotype.Service; - -import lombok.RequiredArgsConstructor; - -@Service -@RequiredArgsConstructor -public class TokenService { - - // TODO : RefreshToken CRUD 구현 -} diff --git a/src/test/java/com/dnd/gongmuin/auth/domain/ProviderTest.java b/src/test/java/com/dnd/gongmuin/auth/domain/ProviderTest.java new file mode 100644 index 00000000..f33eb95c --- /dev/null +++ b/src/test/java/com/dnd/gongmuin/auth/domain/ProviderTest.java @@ -0,0 +1,42 @@ +package com.dnd.gongmuin.auth.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ProviderTest { + + @DisplayName("Provider 이름으로 해당 Enum을 찾을 수 있다.") + @Test + void fromProviderName() { + // given + String kakaoProviderName = "kakao"; + String naverProviderName = "naver"; + + // when + Provider findProvider1 = Provider.fromProviderName(kakaoProviderName); + Provider findProvider2 = Provider.fromProviderName(naverProviderName); + + // then + assertThat(findProvider1).isEqualTo(Provider.KAKAO); + assertThat(findProvider2).isEqualTo(Provider.NAVER); + } + + @DisplayName("소셜 이메일로 해당 Enum을 찾을 수 있다.") + @Test + void fromSocialEmail() { + // given + String kakaoProviderName = "kakao123/kim@daum.net"; + String naverProviderName = "naver123/park@naver.com"; + + // when + Provider findProvider1 = Provider.fromSocialEmail(kakaoProviderName); + Provider findProvider2 = Provider.fromSocialEmail(naverProviderName); + + // then + assertThat(findProvider1).isEqualTo(Provider.KAKAO); + assertThat(findProvider2).isEqualTo(Provider.NAVER); + } + +} diff --git a/src/test/java/com/dnd/gongmuin/common/fixture/MemberFixture.java b/src/test/java/com/dnd/gongmuin/common/fixture/MemberFixture.java index 29ddcde1..e2f0acf1 100644 --- a/src/test/java/com/dnd/gongmuin/common/fixture/MemberFixture.java +++ b/src/test/java/com/dnd/gongmuin/common/fixture/MemberFixture.java @@ -38,6 +38,14 @@ public static Member member2() { ); } + public static Member member3() { + return Member.of( + "소셜회원", + "KAKAO123/member2@daum.net", + 20000 + ); + } + public static Member member(Long memberId) { Member member = Member.of( "김회원", diff --git a/src/test/java/com/dnd/gongmuin/common/support/ApiTestSupport.java b/src/test/java/com/dnd/gongmuin/common/support/ApiTestSupport.java index 9520dece..e10d3a98 100644 --- a/src/test/java/com/dnd/gongmuin/common/support/ApiTestSupport.java +++ b/src/test/java/com/dnd/gongmuin/common/support/ApiTestSupport.java @@ -46,7 +46,7 @@ public void setUpMember() { Member savedMember = memberRepository.save(MemberFixture.member()); AuthInfo authInfo = AuthInfo.of(savedMember.getSocialName(), savedMember.getSocialEmail()); String token = tokenProvider.generateAccessToken(new CustomOauth2User(authInfo), new Date()); - + tokenProvider.generateRefreshToken(new CustomOauth2User(authInfo), new Date()); this.loginMember = savedMember; this.accessToken = "Bearer " + token; } diff --git a/src/test/java/com/dnd/gongmuin/mail/service/MailServiceTest.java b/src/test/java/com/dnd/gongmuin/mail/service/MailServiceTest.java index 070bf484..1059699c 100644 --- a/src/test/java/com/dnd/gongmuin/mail/service/MailServiceTest.java +++ b/src/test/java/com/dnd/gongmuin/mail/service/MailServiceTest.java @@ -9,7 +9,6 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.mail.javamail.JavaMailSender; import com.dnd.gongmuin.common.exception.runtime.NotFoundException; @@ -19,7 +18,6 @@ import com.dnd.gongmuin.mail.dto.response.SendMailResponse; import com.dnd.gongmuin.mail.util.AuthCodeGenerator; import com.dnd.gongmuin.member.repository.MemberRepository; -import com.dnd.gongmuin.member.service.MemberService; import com.dnd.gongmuin.redis.exception.RedisErrorCode; import com.dnd.gongmuin.redis.util.RedisUtil; @@ -32,18 +30,12 @@ class MailServiceTest { @Mock private MemberRepository memberRepository; - @Mock - private MemberService memberService; - @Mock private AuthCodeGenerator authCodeGenerator; @Mock private JavaMailSender mailSender; - @Mock - private RedisTemplate redisTemplate; - @Mock private RedisUtil redisUtil; @@ -59,8 +51,8 @@ void sendToEmail() { String authCode = "123456"; given(authCodeGenerator.createAuthCode()).willReturn(authCode); - given(memberService.isOfficialEmailExists(anyString())).willReturn(false); given(mailSender.createMimeMessage()).willReturn(mimeMessage); + given(memberRepository.existsByOfficialEmail(anyString())).willReturn(false); // when SendMailResponse response = mailService.sendEmail(request); diff --git a/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java b/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java new file mode 100644 index 00000000..0c56c6c6 --- /dev/null +++ b/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java @@ -0,0 +1,117 @@ +package com.dnd.gongmuin.member.controller; + +import static org.springframework.http.HttpHeaders.*; +import static org.springframework.http.MediaType.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.Date; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; + +import com.dnd.gongmuin.common.fixture.MemberFixture; +import com.dnd.gongmuin.common.support.ApiTestSupport; +import com.dnd.gongmuin.member.domain.Member; +import com.dnd.gongmuin.member.dto.request.AdditionalInfoRequest; +import com.dnd.gongmuin.member.dto.request.LogoutRequest; +import com.dnd.gongmuin.member.dto.request.ReissueRequest; +import com.dnd.gongmuin.member.dto.request.ValidateNickNameRequest; +import com.dnd.gongmuin.member.repository.MemberRepository; +import com.dnd.gongmuin.security.jwt.util.TokenProvider; +import com.dnd.gongmuin.security.oauth2.AuthInfo; +import com.dnd.gongmuin.security.oauth2.CustomOauth2User; + +@DisplayName("[MemberController] 통합테스트") +class MemberControllerTest extends ApiTestSupport { + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private TokenProvider tokenProvider; + + @AfterEach + void tearDown() { + redisTemplate.getConnectionFactory().getConnection().flushAll(); + + } + + @DisplayName("닉네임 중복을 검증한다.") + @Test + void checkNickName() throws Exception { + // given + Member member = MemberFixture.member2(); + memberRepository.save(member); + ValidateNickNameRequest request = new ValidateNickNameRequest("회원"); + + // when // then + mockMvc.perform(post("/api/auth/check-nickname") + .content(toJson(request)) + .contentType(APPLICATION_JSON) + .header(AUTHORIZATION, accessToken) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("isDuplicated").value(true)); + } + + @DisplayName("추가 정보를 기입해 회원가입을 진행한다.") + @Test + void signUp() throws Exception { + // given + AdditionalInfoRequest request = new AdditionalInfoRequest("dsaf@korea.kr", "회원", "공업", "가스"); + + Member savedMember = memberRepository.save(MemberFixture.member3()); + AuthInfo authInfo = AuthInfo.of(savedMember.getSocialName(), savedMember.getSocialEmail()); + String token = tokenProvider.generateAccessToken(new CustomOauth2User(authInfo), new Date()); + this.loginMember = savedMember; + this.accessToken = "Bearer " + token; + + // when // then + mockMvc.perform(post("/api/auth/member") + .content(toJson(request)) + .contentType(APPLICATION_JSON) + .header(AUTHORIZATION, accessToken) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("nickName").value("회원")); + } + + @DisplayName("로그인 된 회원은 로그아웃을 할 수 있다.") + @Test + void logout() throws Exception { + // given + LogoutRequest request = new LogoutRequest(accessToken); + + // when // then + mockMvc.perform(post("/api/auth/logout") + .content(toJson(request)) + .contentType(APPLICATION_JSON) + .header(AUTHORIZATION, accessToken) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("result").value("true")); + } + + @DisplayName("토큰을 재발급 할 수 있다.") + @Test + void reissue() throws Exception { + // given + ReissueRequest request = new ReissueRequest(accessToken); + + // when // then + mockMvc.perform(post("/api/auth/reissue/token") + .content(toJson(request)) + .contentType(APPLICATION_JSON) + .header(AUTHORIZATION, accessToken) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("accessToken").isString()); + } +} diff --git a/src/test/java/com/dnd/gongmuin/member/service/MemberServiceTest.java b/src/test/java/com/dnd/gongmuin/member/service/MemberServiceTest.java index 43a6354b..0c7f6000 100644 --- a/src/test/java/com/dnd/gongmuin/member/service/MemberServiceTest.java +++ b/src/test/java/com/dnd/gongmuin/member/service/MemberServiceTest.java @@ -3,44 +3,71 @@ import static com.dnd.gongmuin.member.domain.JobCategory.*; import static com.dnd.gongmuin.member.domain.JobGroup.*; import static org.assertj.core.api.Assertions.*; +import static org.mockito.BDDMockito.*; + +import java.time.Duration; +import java.util.Date; +import java.util.Optional; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.annotation.Transactional; - -import com.dnd.gongmuin.common.support.ApiTestSupport; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; + +import com.dnd.gongmuin.auth.domain.Provider; +import com.dnd.gongmuin.common.fixture.MemberFixture; import com.dnd.gongmuin.member.domain.Member; import com.dnd.gongmuin.member.dto.request.AdditionalInfoRequest; +import com.dnd.gongmuin.member.dto.request.LogoutRequest; +import com.dnd.gongmuin.member.dto.request.ReissueRequest; import com.dnd.gongmuin.member.dto.request.ValidateNickNameRequest; +import com.dnd.gongmuin.member.dto.response.LogoutResponse; +import com.dnd.gongmuin.member.dto.response.ReissueResponse; import com.dnd.gongmuin.member.dto.response.ValidateNickNameResponse; import com.dnd.gongmuin.member.repository.MemberRepository; +import com.dnd.gongmuin.redis.util.RedisUtil; +import com.dnd.gongmuin.security.jwt.util.TokenProvider; +import com.dnd.gongmuin.security.oauth2.CustomOauth2User; + +@ExtendWith(MockitoExtension.class) +class MemberServiceTest { -@Transactional -@Disabled -class MemberServiceTest extends ApiTestSupport { + @Mock + private MemberRepository memberRepository; - @Autowired - MemberService memberService; + @Mock + private TokenProvider tokenProvider; - @Autowired - MemberRepository memberRepository; + @Mock + private RedisUtil redisUtil; + + @InjectMocks + private MemberService memberService; @DisplayName("조합된 소셜 이메일 부분 중 공급자 부분을 얻을 수 있다.") @Test void parseProviderFromSocialEmail() { // given - Member kakaoMember = createMember("김철수", "철수", "kakao123/kakao123@daum.net", "abc123@korea.com"); - Member naverMember = createMember("김철수", "철수", "naver123/naver123@naver.com", "abc321@korea.com"); + Member kakaoMember = createMember("김철수", "철수", "kakao123/kim@daum.net", "abc123@korea.com"); + Member naverMember = createMember("박철수", "철수", "naver123/park@naver.com", "abc321@korea.com"); - // when - String kakaoProvider = memberService.parseProviderFromSocialEmail(kakaoMember); - String naverProvider = memberService.parseProviderFromSocialEmail(naverMember); + try (MockedStatic mockedProvider = mockStatic(Provider.class)) { + mockedProvider.when(() -> Provider.fromSocialEmail("kakao123/kim@daum.net")).thenReturn(Provider.KAKAO); + mockedProvider.when(() -> Provider.fromSocialEmail("naver123/park@naver.com")).thenReturn(Provider.NAVER); - // then - assertThat(kakaoProvider).isEqualToIgnoringCase("kakao"); - assertThat(naverProvider).isEqualToIgnoringCase("naver"); + // when + Provider kakaoProvider = memberService.parseProviderFromSocialEmail(kakaoMember); + Provider naverProvider = memberService.parseProviderFromSocialEmail(naverMember); + + // then + assertThat(kakaoProvider).isEqualTo(Provider.KAKAO); + assertThat(naverProvider).isEqualTo(Provider.NAVER); + } } @DisplayName("공무원 이메일이 존재하는지 체크한다.") @@ -61,9 +88,7 @@ void isOfficialEmail() { @Test void isDuplicatedNickname() { // given - Member member1 = createMember("김철수", "철수", "kakao123/kakao123@daum.net", "abc123@korea.com"); - memberRepository.save(member1); - + given(memberRepository.existsByNickname("김철수")).willReturn(true); ValidateNickNameRequest request = new ValidateNickNameRequest("김철수"); // when @@ -77,11 +102,12 @@ void isDuplicatedNickname() { @Test void signUp() { // given - Member member1 = createMember(null, "철수", "kakao123/kakao123@daum.net", null); - memberRepository.save(member1); - AdditionalInfoRequest request = new AdditionalInfoRequest("abc123@korea.com", "김신규", "공업", "가스"); + Member member1 = createMember(null, "철수", "kakao123/kakao123@daum.net", null); + given(memberRepository.findBySocialEmail("kakao123/kakao123@daum.net")).willReturn( + Optional.ofNullable(member1)); + // when memberService.signUp(request, "kakao123/kakao123@daum.net"); @@ -96,6 +122,72 @@ void signUp() { } + @DisplayName("소셜 이메일로 회원을 찾는다.") + @Test + void getMemberBySocialEmail() { + // given + Member member = createMember(null, "김회원", "kakao123/kakao123@daum.net", null); + given(memberRepository.findBySocialEmail("kakao123/kakao123@daum.net")).willReturn(Optional.ofNullable(member)); + + // when + Member findMember = memberService.getMemberBySocialEmail("kakao123/kakao123@daum.net"); + + // then + assertThat(findMember).extracting("socialName", "socialEmail") + .containsExactlyInAnyOrder( + "김회원", + "kakao123/kakao123@daum.net" + ); + } + + @DisplayName("로그인 회원은 로그아웃 할 수 있다.") + @Test + void logout() { + // given + Member principal = MemberFixture.member(); + Authentication authentication = new UsernamePasswordAuthenticationToken(principal, "test"); + LogoutRequest request = new LogoutRequest("Bearer dsaooinbsoadi"); + Long fiveMinutes = 300_000L; + + given(tokenProvider.validateToken(anyString(), any(Date.class))).willReturn(true); + given(tokenProvider.getAuthentication(anyString())).willReturn(authentication); + given(tokenProvider.getExpiration(anyString(), any(Date.class))).willReturn(fiveMinutes); + + given(redisUtil.getValues(anyString())).willReturn("refresh"); + willDoNothing().given(redisUtil).setValues(anyString(), anyString(), any(Duration.class)); + + given(redisUtil.getValues(anyString())).willReturn("logout"); + // when + LogoutResponse response = memberService.logout(request); + + // then + assertThat(response.result()).isTrue(); + } + + @DisplayName("refresh 토큰이 만료되지 않았다면 재발급 할 수 있다.") + @Test + void reissue() { + // given + ReissueRequest request = new ReissueRequest("Bearer dafdfweqe"); + Member principal = MemberFixture.member(); + Authentication authentication = new UsernamePasswordAuthenticationToken(principal, "test"); + + given(tokenProvider.validateToken(anyString(), any(Date.class))).willReturn(true); + given(tokenProvider.getAuthentication(anyString())).willReturn(authentication); + + given(redisUtil.getValues(anyString())).willReturn("refreshToken"); + given(tokenProvider.generateAccessToken(any(CustomOauth2User.class), any(Date.class))).willReturn( + "reissueToken"); + given(tokenProvider.generateRefreshToken(any(CustomOauth2User.class), any(Date.class))).willReturn( + "reissueToken"); + + // when + ReissueResponse response = memberService.reissue(request); + + // then + assertThat(response.accessToken()).isEqualTo("reissueToken"); + } + private Member createMember(String nickname, String socialName, String socialEmail, String officialEmail) { return Member.builder() .nickname(nickname) diff --git a/src/test/java/com/dnd/gongmuin/security/jwt/TokenProviderTest.java b/src/test/java/com/dnd/gongmuin/security/jwt/TokenProviderTest.java index 9b5e2613..f7c9f033 100644 --- a/src/test/java/com/dnd/gongmuin/security/jwt/TokenProviderTest.java +++ b/src/test/java/com/dnd/gongmuin/security/jwt/TokenProviderTest.java @@ -1,61 +1,59 @@ package com.dnd.gongmuin.security.jwt; import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; -import static org.mockito.MockitoAnnotations.*; +import static org.mockito.BDDMockito.*; import java.util.Date; +import java.util.Optional; import javax.crypto.SecretKey; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.transaction.annotation.Transactional; +import com.dnd.gongmuin.common.fixture.MemberFixture; +import com.dnd.gongmuin.member.domain.Member; +import com.dnd.gongmuin.member.repository.MemberRepository; +import com.dnd.gongmuin.redis.util.RedisUtil; import com.dnd.gongmuin.security.jwt.util.TokenProvider; import com.dnd.gongmuin.security.oauth2.AuthInfo; import com.dnd.gongmuin.security.oauth2.CustomOauth2User; -import com.dnd.gongmuin.security.service.TokenService; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; -@Transactional @ExtendWith(MockitoExtension.class) -@Disabled class TokenProviderTest { - @Value("${spring.jwt.key}") private String key; - - @InjectMocks - private TokenProvider tokenProvider; + private SecretKey secretKey; + private AuthInfo authInfo; @Mock - private TokenService tokenService; - - private SecretKey secretKey; + private RedisUtil redisUtil; @Mock - private AuthInfo authInfo; + private MemberRepository memberRepository; + + @InjectMocks + private TokenProvider tokenProvider; @BeforeEach void setUp() { - openMocks(this); - + key = "oeq213n214eqf141n161saf145125t12tg2er31t3241g4v2r3131351332dsafsawefewqrft23fewfvdsafdsf32e1wq1r3ewfedfasdfsdafqrewqr1"; secretKey = Keys.hmacShaKeyFor(key.getBytes()); ReflectionTestUtils.setField(tokenProvider, "secretKey", secretKey); + + this.authInfo = AuthInfo.of("김회원", "kakao123/daum.net"); } @DisplayName("만료일이 30분인 토큰이 생성된다.") @@ -65,18 +63,16 @@ void generateAccessToken() { Date now = new Date(); long expectedExpirationTime = now.getTime() + 30 * 60 * 1000; - when(authInfo.getSocialEmail()).thenReturn("kakao123/kimMember@daum.net"); - when(authInfo.getSocialName()).thenReturn("김회원"); CustomOauth2User authentication = new CustomOauth2User(authInfo); // when String accessToken = tokenProvider.generateAccessToken(authentication, now); Claims claims = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(accessToken).getPayload(); - Date expiration = claims.getExpiration(); // then assertThat(expiration.getTime()).isCloseTo(expectedExpirationTime, within(1000L)); + } @DisplayName("만료일이 1일인 토큰이 생성된다.") @@ -86,14 +82,11 @@ void generateRefreshToken() { Date now = new Date(); long expectedExpirationTime = now.getTime() + 1000 * 60 * 60 * 24; - when(authInfo.getSocialEmail()).thenReturn("kakao123/kimMember@daum.net"); - when(authInfo.getSocialName()).thenReturn("김회원"); CustomOauth2User authentication = new CustomOauth2User(authInfo); // when String accessToken = tokenProvider.generateRefreshToken(authentication, now); Claims claims = Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(accessToken).getPayload(); - Date expiration = claims.getExpiration(); // then @@ -106,18 +99,19 @@ void getAuthentication() { // given Date now = new Date(); - when(authInfo.getSocialEmail()).thenReturn("kakao123/kimMember@daum.net"); - when(authInfo.getSocialName()).thenReturn("김회원"); + Member member = MemberFixture.member(); CustomOauth2User customOauth2User = new CustomOauth2User(authInfo); - String accessToken = tokenProvider.generateRefreshToken(customOauth2User, now); + String accessToken = tokenProvider.generateAccessToken(customOauth2User, now); + + given(memberRepository.findBySocialEmail(anyString())).willReturn(Optional.ofNullable(member)); // when Authentication authentication = tokenProvider.getAuthentication(accessToken); - CustomOauth2User getPrincipal = (CustomOauth2User)authentication.getPrincipal(); + Member principal = (Member)authentication.getPrincipal(); // then assertThat(authentication.isAuthenticated()).isTrue(); - assertThat(getPrincipal.getEmail()).isEqualTo("kakao123/kimMember@daum.net"); + assertThat(principal.getSocialEmail()).isEqualTo("KAKAO123/gongmuin@daum.net"); } @DisplayName("토큰의 만료일이 현재 시간보다 전이면 만료된 토큰이다.") @@ -126,8 +120,6 @@ void validateToken() { // given Date past = new Date(124, 6, 30, 16, 0, 0); - when(authInfo.getSocialEmail()).thenReturn("kakao123/kimMember@daum.net"); - when(authInfo.getSocialName()).thenReturn("김회원"); CustomOauth2User customOauth2User = new CustomOauth2User(authInfo); String accessToken = tokenProvider.generateRefreshToken(customOauth2User, past);