From 2fbef2f4cafce4feba59bf092a63b00514c73be2 Mon Sep 17 00:00:00 2001 From: dudxo Date: Fri, 9 Aug 2024 21:06:55 +0900 Subject: [PATCH 01/41] =?UTF-8?q?[fix]=20:=20TokenService=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dnd/gongmuin/security/service/TokenService.java | 12 ------------ .../dnd/gongmuin/security/jwt/TokenProviderTest.java | 4 ---- 2 files changed, 16 deletions(-) delete mode 100644 src/main/java/com/dnd/gongmuin/security/service/TokenService.java 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/security/jwt/TokenProviderTest.java b/src/test/java/com/dnd/gongmuin/security/jwt/TokenProviderTest.java index 9b5e2613..46d8c528 100644 --- a/src/test/java/com/dnd/gongmuin/security/jwt/TokenProviderTest.java +++ b/src/test/java/com/dnd/gongmuin/security/jwt/TokenProviderTest.java @@ -24,7 +24,6 @@ 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; @@ -41,9 +40,6 @@ class TokenProviderTest { @InjectMocks private TokenProvider tokenProvider; - @Mock - private TokenService tokenService; - private SecretKey secretKey; @Mock From d3ece7759cb4a552f8344afb7ba574dd5fada655 Mon Sep 17 00:00:00 2001 From: dudxo Date: Fri, 9 Aug 2024 21:07:50 +0900 Subject: [PATCH 02/41] =?UTF-8?q?[feat]=20:=20=EC=86=8C=EC=85=9C=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=84=B1=EA=B3=B5=20=EC=8B=9C=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=83=9D=EC=84=B1=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20-=20refreshToken=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20-=20refreshToken=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=8B=9C=20redis=20=EC=A0=80=EC=9E=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/handler/CustomOauth2SuccessHandler.java | 2 ++ .../dnd/gongmuin/security/jwt/util/TokenProvider.java | 10 +++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) 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/TokenProvider.java b/src/main/java/com/dnd/gongmuin/security/jwt/util/TokenProvider.java index 899d0a15..1e5e3cfd 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; @@ -19,9 +20,9 @@ import com.dnd.gongmuin.common.exception.runtime.CustomJwtException; import com.dnd.gongmuin.member.domain.Member; import com.dnd.gongmuin.member.service.MemberService; +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 +39,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 RedisUtil redisUtil; @Value("${spring.jwt.key}") private String key; private SecretKey secretKey; @@ -55,7 +56,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; } From c5baa491f7287dfc12555ed3bbb8754f135dfa2b Mon Sep 17 00:00:00 2001 From: dudxo Date: Fri, 9 Aug 2024 22:00:34 +0900 Subject: [PATCH 03/41] =?UTF-8?q?[feat]=20:=20Token=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dnd/gongmuin/auth/exception/AuthErrorCode.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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; From 0fe6a8395df1cebaac072db6ae287f1c00439974 Mon Sep 17 00:00:00 2001 From: dudxo Date: Fri, 9 Aug 2024 22:01:20 +0900 Subject: [PATCH 04/41] =?UTF-8?q?[feat]=20:=20=EB=82=A8=EC=9D=80=20?= =?UTF-8?q?=EB=A7=8C=EB=A3=8C=EC=8B=9C=EA=B0=84=20=EB=B0=98=ED=99=98=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/dnd/gongmuin/security/jwt/util/TokenProvider.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 1e5e3cfd..11d4a9b7 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 @@ -126,4 +126,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()); + } + } From db8ea1ca5109e08a56d3f69c80f567612a05122a Mon Sep 17 00:00:00 2001 From: dudxo Date: Fri, 9 Aug 2024 22:01:43 +0900 Subject: [PATCH 05/41] =?UTF-8?q?[feat]=20:=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 10 ++++++ .../member/dto/request/LogoutRequest.java | 10 ++++++ .../member/dto/response/LogoutResponse.java | 6 ++++ .../member/service/MemberService.java | 31 +++++++++++++++++++ 4 files changed, 57 insertions(+) create mode 100644 src/main/java/com/dnd/gongmuin/member/dto/request/LogoutRequest.java create mode 100644 src/main/java/com/dnd/gongmuin/member/dto/response/LogoutResponse.java 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..331c5383 100644 --- a/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java +++ b/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java @@ -2,18 +2,22 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.dnd.gongmuin.member.dto.request.AdditionalInfoRequest; +import com.dnd.gongmuin.member.dto.request.LogoutRequest; import com.dnd.gongmuin.member.dto.request.ValidateNickNameRequest; +import com.dnd.gongmuin.member.dto.response.LogoutResponse; 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 @@ -37,4 +41,10 @@ public ResponseEntity signUp(@RequestBody AdditionalInfoRequest return ResponseEntity.ok(response); } + @GetMapping("/logout") + public ResponseEntity logout(@Valid LogoutRequest request) { + LogoutResponse response = memberService.logout(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..fb065728 --- /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 = "잘못된 요청입니다.") + 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/service/MemberService.java b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java index 78184133..d027fc52 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,29 @@ 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.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.ValidateNickNameRequest; +import com.dnd.gongmuin.member.dto.response.LogoutResponse; 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.Oauth2Response; import lombok.RequiredArgsConstructor; @@ -24,7 +32,10 @@ @RequiredArgsConstructor public class MemberService { + private static final String TOKEN_PREFIX = "Bearer "; private final MemberRepository memberRepository; + private final TokenProvider tokenProvider; + private final RedisUtil redisUtil; public Member saveOrUpdate(Oauth2Response oauth2Response) { Member member = memberRepository.findBySocialEmail(oauth2Response.createSocialEmail()) @@ -98,4 +109,24 @@ public boolean isOfficialEmailExists(String officialEmail) { return result; } + + 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)); + + return new LogoutResponse(true); + } } \ No newline at end of file From 0526c5dd2cada12c8ea9d2212148929ba1ae1231 Mon Sep 17 00:00:00 2001 From: dudxo Date: Fri, 9 Aug 2024 22:02:26 +0900 Subject: [PATCH 06/41] =?UTF-8?q?[feat]=20:=20DTO=20Valid=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/dnd/gongmuin/member/controller/MemberController.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 331c5383..65f181b5 100644 --- a/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java +++ b/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java @@ -29,12 +29,13 @@ 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, + public ResponseEntity signUp( + @RequestBody @Valid AdditionalInfoRequest request, @AuthenticationPrincipal CustomOauth2User loginMember) { SignUpResponse response = memberService.signUp(request, loginMember.getEmail()); From b2bd9601252afad26f7787f45e095976953ef15f Mon Sep 17 00:00:00 2001 From: dudxo Date: Fri, 9 Aug 2024 22:11:03 +0900 Subject: [PATCH 07/41] =?UTF-8?q?[fix]=20:=20logout=20HttpMethod=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20RequestBody=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/dnd/gongmuin/member/controller/MemberController.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 65f181b5..d03fbbb3 100644 --- a/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java +++ b/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java @@ -2,7 +2,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -42,8 +41,8 @@ public ResponseEntity signUp( return ResponseEntity.ok(response); } - @GetMapping("/logout") - public ResponseEntity logout(@Valid LogoutRequest request) { + @PostMapping("/logout") + public ResponseEntity logout(@RequestBody @Valid LogoutRequest request) { LogoutResponse response = memberService.logout(request); return ResponseEntity.ok(response); } From 8da544b6bec78c46cccf875357b3f7e936c2deae Mon Sep 17 00:00:00 2001 From: dudxo Date: Fri, 9 Aug 2024 22:15:18 +0900 Subject: [PATCH 08/41] =?UTF-8?q?[feat]=20:=20accessToken=20logout=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/jwt/util/TokenAuthenticationFilter.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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); From 23f52146bef32e6f087f37ffa9b03d966c25d5f6 Mon Sep 17 00:00:00 2001 From: dudxo Date: Fri, 9 Aug 2024 22:41:04 +0900 Subject: [PATCH 09/41] =?UTF-8?q?[feat]=20:=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=9E=AC=EB=B0=9C=EA=B8=89=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 6 ++++ .../member/dto/request/ReissueRequest.java | 9 ++++++ .../member/dto/response/ReissueResponse.java | 6 ++++ .../member/service/MemberService.java | 30 +++++++++++++++++++ 4 files changed, 51 insertions(+) create mode 100644 src/main/java/com/dnd/gongmuin/member/dto/request/ReissueRequest.java create mode 100644 src/main/java/com/dnd/gongmuin/member/dto/response/ReissueResponse.java 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 d03fbbb3..d85c73c6 100644 --- a/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java +++ b/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java @@ -9,6 +9,7 @@ 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.SignUpResponse; @@ -47,4 +48,9 @@ public ResponseEntity logout(@RequestBody @Valid LogoutRequest r return ResponseEntity.ok(response); } + @PostMapping("/reissue/token") + public ResponseEntity reissue(@RequestBody @Valid ReissueRequest request) { + ReissueRequest response = memberService.reissue(request); + return ResponseEntity.ok(response); + } } 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..00f6b1b5 --- /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 = "잘못된 요청입니다.") + String accessToken +) { +} 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/service/MemberService.java b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java index d027fc52..2eeeb084 100644 --- a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java +++ b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java @@ -16,6 +16,7 @@ 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.SignUpResponse; @@ -24,6 +25,7 @@ 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; import com.dnd.gongmuin.security.oauth2.Oauth2Response; import lombok.RequiredArgsConstructor; @@ -129,4 +131,32 @@ public LogoutResponse logout(LogoutRequest request) { return new LogoutResponse(true); } + + public ReissueRequest 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); + } + + String reissuedAccessToken = tokenProvider.generateAccessToken((CustomOauth2User)authentication, new Date()); + tokenProvider.generateRefreshToken((CustomOauth2User)authentication, new Date()); + + return new ReissueRequest(reissuedAccessToken); + } } \ No newline at end of file From 0de33c0bcf1ce366a864357616deac3072b2b13e Mon Sep 17 00:00:00 2001 From: dudxo Date: Sat, 10 Aug 2024 11:41:20 +0900 Subject: [PATCH 10/41] =?UTF-8?q?[refactor]=20:=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EA=B0=90=EC=A7=80=EB=A5=BC=20=EC=9D=B4=EC=9A=A9=ED=95=9C=20Res?= =?UTF-8?q?ponseDTO=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dnd/gongmuin/member/service/MemberService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 2eeeb084..9975f915 100644 --- a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java +++ b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java @@ -84,9 +84,9 @@ public SignUpResponse signUp(AdditionalInfoRequest request, String email) { new NotFoundException(MemberErrorCode.NOT_FOUND_NEW_MEMBER); } - Member signUpMember = updateAdditionalInfo(request, findMember); + updateAdditionalInfo(request, findMember); - return new SignUpResponse(signUpMember.getNickname()); + return new SignUpResponse(findMember.getNickname()); } public Member getMemberBySocialEmail(String socialEmail) { From 5f77c6eb72ce5279d524c543f9bc6c286b68d617 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sat, 10 Aug 2024 11:42:12 +0900 Subject: [PATCH 11/41] =?UTF-8?q?[test]=20:=20Mocking=EC=9D=84=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20Service=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/MemberServiceTest.java | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) 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..0e42b0c4 100644 --- a/src/test/java/com/dnd/gongmuin/member/service/MemberServiceTest.java +++ b/src/test/java/com/dnd/gongmuin/member/service/MemberServiceTest.java @@ -3,30 +3,32 @@ 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.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 org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; -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.ValidateNickNameRequest; import com.dnd.gongmuin.member.dto.response.ValidateNickNameResponse; import com.dnd.gongmuin.member.repository.MemberRepository; -@Transactional -@Disabled -class MemberServiceTest extends ApiTestSupport { - - @Autowired - MemberService memberService; +@ExtendWith(MockitoExtension.class) +class MemberServiceTest { - @Autowired + @Mock MemberRepository memberRepository; + @InjectMocks + MemberService memberService; + @DisplayName("조합된 소셜 이메일 부분 중 공급자 부분을 얻을 수 있다.") @Test void parseProviderFromSocialEmail() { @@ -61,9 +63,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,16 +77,18 @@ 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", "김신규", "공업", "가스"); + Optional member1 = Optional.ofNullable(createMember(null, "철수", "kakao123/kakao123@daum.net", null)); + given(memberRepository.findBySocialEmail("kakao123/kakao123@daum.net")).willReturn(member1); + Member member = member1.get(); + given(memberRepository.save(any(Member.class))).willReturn(member); + // when memberService.signUp(request, "kakao123/kakao123@daum.net"); // then - assertThat(member1).extracting("officialEmail", "nickname", "jobGroup", "jobCategory") + assertThat(member).extracting("officialEmail", "nickname", "jobGroup", "jobCategory") .containsExactlyInAnyOrder( "abc123@korea.com", "김신규", From 840550eee9f5163249af77891dddfdc1a26063f8 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sat, 10 Aug 2024 12:39:13 +0900 Subject: [PATCH 12/41] =?UTF-8?q?[feat]=20:=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=8B=A4=ED=8C=A8=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/dnd/gongmuin/member/service/MemberService.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 9975f915..4cb227ef 100644 --- a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java +++ b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java @@ -35,6 +35,7 @@ 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; @@ -127,7 +128,12 @@ public LogoutResponse logout(LogoutRequest request) { } Long expiration = tokenProvider.getExpiration(accessToken, new Date()); - redisUtil.setValues(accessToken, "logout", Duration.ofMillis(expiration)); + redisUtil.setValues(accessToken, LOGOUT, Duration.ofMillis(expiration)); + + String values = redisUtil.getValues(accessToken); + if (!Objects.equals(values, LOGOUT)) { + throw new NotFoundException(MemberErrorCode.FAIL_LOGOUT); + } return new LogoutResponse(true); } From 4866b23fc772632a4362cd13e2064a2a7942b112 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sat, 10 Aug 2024 12:39:30 +0900 Subject: [PATCH 13/41] =?UTF-8?q?[feat]=20:=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=97=90=EB=9F=AC=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/dnd/gongmuin/member/exception/MemberErrorCode.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 19ffd702..433c7486 100644 --- a/src/main/java/com/dnd/gongmuin/member/exception/MemberErrorCode.java +++ b/src/main/java/com/dnd/gongmuin/member/exception/MemberErrorCode.java @@ -10,7 +10,8 @@ public enum MemberErrorCode implements ErrorCode { NOT_FOUND_MEMBER("특정 회원을 찾을 수 없습니다.", "MEMBER_001"), - NOT_FOUND_NEW_MEMBER("신규 회원이 아닙니다.", "MEMBER_002"); + NOT_FOUND_NEW_MEMBER("신규 회원이 아닙니다.", "MEMBER_002"), + FAIL_LOGOUT("로그아웃을 실패했습니다.", "MEMBER_003"); private final String message; private final String code; From cfaa693042f548b8b10ad4751c85a58b83409685 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sat, 10 Aug 2024 14:42:38 +0900 Subject: [PATCH 14/41] =?UTF-8?q?[fix]=20:=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=9E=AC=EB=B0=9C=EA=B8=89=20=EB=B0=98=ED=99=98=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20DTO=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/dnd/gongmuin/member/controller/MemberController.java | 5 +++-- .../java/com/dnd/gongmuin/member/service/MemberService.java | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) 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 d85c73c6..b9470095 100644 --- a/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java +++ b/src/main/java/com/dnd/gongmuin/member/controller/MemberController.java @@ -12,6 +12,7 @@ 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; @@ -49,8 +50,8 @@ public ResponseEntity logout(@RequestBody @Valid LogoutRequest r } @PostMapping("/reissue/token") - public ResponseEntity reissue(@RequestBody @Valid ReissueRequest request) { - ReissueRequest response = memberService.reissue(request); + 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/service/MemberService.java b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java index 4cb227ef..79286317 100644 --- a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java +++ b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java @@ -19,6 +19,7 @@ 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; @@ -138,7 +139,7 @@ public LogoutResponse logout(LogoutRequest request) { return new LogoutResponse(true); } - public ReissueRequest reissue(ReissueRequest request) { + public ReissueResponse reissue(ReissueRequest request) { String accessToken = request.accessToken().substring(TOKEN_PREFIX.length()); if (!tokenProvider.validateToken(accessToken, new Date())) { @@ -163,6 +164,6 @@ public ReissueRequest reissue(ReissueRequest request) { String reissuedAccessToken = tokenProvider.generateAccessToken((CustomOauth2User)authentication, new Date()); tokenProvider.generateRefreshToken((CustomOauth2User)authentication, new Date()); - return new ReissueRequest(reissuedAccessToken); + return new ReissueResponse(reissuedAccessToken); } } \ No newline at end of file From ccbccf3697b951f3e4d59aa404ca3337d15e2309 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sat, 10 Aug 2024 18:22:04 +0900 Subject: [PATCH 15/41] =?UTF-8?q?[fix]=20:=20API=20'/'=20=EB=88=84?= =?UTF-8?q?=EB=9D=BD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dnd/gongmuin/auth/cotroller/AuthController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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(); // 카카오 로그인 페이지로 리다이렉트 From f3f2c8182b67eee97638e737f2bc6785439ce0e4 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sat, 10 Aug 2024 18:22:59 +0900 Subject: [PATCH 16/41] =?UTF-8?q?[feat]=20:=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=9E=AC=EB=B0=9C=EA=B8=89=20=EC=8B=9C=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EC=9D=B8=EC=A6=9D=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/dnd/gongmuin/member/service/MemberService.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 79286317..e0e115dc 100644 --- a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java +++ b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java @@ -26,6 +26,7 @@ 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; @@ -161,8 +162,10 @@ public ReissueResponse reissue(ReissueRequest request) { throw new ValidationException(AuthErrorCode.UNAUTHORIZED_TOKEN); } - String reissuedAccessToken = tokenProvider.generateAccessToken((CustomOauth2User)authentication, new Date()); - tokenProvider.generateRefreshToken((CustomOauth2User)authentication, new Date()); + CustomOauth2User customUser = new CustomOauth2User( + AuthInfo.of(member.getSocialName(), member.getSocialEmail())); + String reissuedAccessToken = tokenProvider.generateAccessToken(customUser, new Date()); + tokenProvider.generateRefreshToken(customUser, new Date()); return new ReissueResponse(reissuedAccessToken); } From 427ea4bc67b423407051a8358d3f66b8b047236c Mon Sep 17 00:00:00 2001 From: dudxo Date: Sat, 10 Aug 2024 18:23:38 +0900 Subject: [PATCH 17/41] =?UTF-8?q?[fix]=20:=20MeberService.class=20<->=20To?= =?UTF-8?q?kenProvider.class=20=EC=88=9C=ED=99=98=EC=B0=B8=EC=A1=B0=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dnd/gongmuin/security/jwt/util/TokenProvider.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 11d4a9b7..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 @@ -18,8 +18,10 @@ 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; @@ -39,7 +41,7 @@ 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 MemberService memberService; + private final MemberRepository memberRepository; private final RedisUtil redisUtil; @Value("${spring.jwt.key}") private String key; @@ -91,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); } From 21043a1faa43a5456d71da6353dba6294a6ac71a Mon Sep 17 00:00:00 2001 From: dudxo Date: Sat, 10 Aug 2024 18:24:10 +0900 Subject: [PATCH 18/41] =?UTF-8?q?[test]=20:=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83,=20=ED=86=A0=ED=81=B0=20=EC=9E=AC=EB=B0=9C=EA=B8=89?= =?UTF-8?q?=20=EB=8B=A8=EC=9C=84=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/MemberServiceTest.java | 98 +++++++++++++++++-- 1 file changed, 91 insertions(+), 7 deletions(-) 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 0e42b0c4..59f1f347 100644 --- a/src/test/java/com/dnd/gongmuin/member/service/MemberServiceTest.java +++ b/src/test/java/com/dnd/gongmuin/member/service/MemberServiceTest.java @@ -5,6 +5,8 @@ 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.DisplayName; @@ -13,21 +15,37 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +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 { @Mock - MemberRepository memberRepository; + private MemberRepository memberRepository; + + @Mock + private TokenProvider tokenProvider; + + @Mock + private RedisUtil redisUtil; @InjectMocks - MemberService memberService; + private MemberService memberService; @DisplayName("조합된 소셜 이메일 부분 중 공급자 부분을 얻을 수 있다.") @Test @@ -79,16 +97,16 @@ void signUp() { // given AdditionalInfoRequest request = new AdditionalInfoRequest("abc123@korea.com", "김신규", "공업", "가스"); - Optional member1 = Optional.ofNullable(createMember(null, "철수", "kakao123/kakao123@daum.net", null)); - given(memberRepository.findBySocialEmail("kakao123/kakao123@daum.net")).willReturn(member1); - Member member = member1.get(); - given(memberRepository.save(any(Member.class))).willReturn(member); + Member member1 = createMember(null, "철수", "kakao123/kakao123@daum.net", null); + given(memberRepository.findBySocialEmail("kakao123/kakao123@daum.net")).willReturn( + Optional.ofNullable(member1)); + given(memberRepository.save(any(Member.class))).willReturn(member1); // when memberService.signUp(request, "kakao123/kakao123@daum.net"); // then - assertThat(member).extracting("officialEmail", "nickname", "jobGroup", "jobCategory") + assertThat(member1).extracting("officialEmail", "nickname", "jobGroup", "jobCategory") .containsExactlyInAnyOrder( "abc123@korea.com", "김신규", @@ -98,6 +116,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) From 5c1942e9cf6d3a8c5d6de9785c22059db86d61a8 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sat, 10 Aug 2024 21:45:35 +0900 Subject: [PATCH 19/41] =?UTF-8?q?[fix]=20:=20@AuthenticationPrincipal?= =?UTF-8?q?=EB=A5=BC=20=ED=86=B5=ED=95=B4=20=ED=9A=8D=EB=93=9D=ED=95=9C=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EA=B0=9D=EC=B2=B4=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dnd/gongmuin/member/controller/MemberController.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 b9470095..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,6 +7,7 @@ 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; @@ -16,7 +17,6 @@ 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; @@ -37,8 +37,8 @@ public ResponseEntity checkNickName( @PostMapping("/member") public ResponseEntity signUp( @RequestBody @Valid AdditionalInfoRequest request, - @AuthenticationPrincipal CustomOauth2User loginMember) { - SignUpResponse response = memberService.signUp(request, loginMember.getEmail()); + @AuthenticationPrincipal Member loginMember) { + SignUpResponse response = memberService.signUp(request, loginMember.getSocialEmail()); return ResponseEntity.ok(response); } From 0ad361fb7a73ea0920040c90d5befef7ed06cb18 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sat, 10 Aug 2024 22:22:04 +0900 Subject: [PATCH 20/41] =?UTF-8?q?[test]=20:=20setUp=EC=8B=9C=20refreshToke?= =?UTF-8?q?n=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dnd/gongmuin/common/support/ApiTestSupport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; } From cb40730c421a42f08c3b345e3ef85a0f66e6f30c Mon Sep 17 00:00:00 2001 From: dudxo Date: Sat, 10 Aug 2024 22:22:29 +0900 Subject: [PATCH 21/41] =?UTF-8?q?[test]=20:=20MemberController=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MemberControllerTest.java | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java 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..603ea655 --- /dev/null +++ b/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java @@ -0,0 +1,101 @@ +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 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.transaction.annotation.Transactional; + +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; + +@Transactional +@DisplayName("[MemberController] 통합테스트") +class MemberControllerTest extends ApiTestSupport { + + @Autowired + private MemberRepository memberRepository; + + @AfterEach + void tearDown() { + memberRepository.deleteAll(); + } + + @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", "회원", "공업", "가스"); + + // 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()); + } + +} From 4b6dfdc92d59fae153d4d080c29f5afb0cfb1cdd Mon Sep 17 00:00:00 2001 From: dudxo Date: Sat, 10 Aug 2024 23:10:17 +0900 Subject: [PATCH 22/41] =?UTF-8?q?[test]=20:=20=EA=B0=81=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=ED=9B=84=20redis=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberControllerTest.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java b/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java index 603ea655..09fe4ecf 100644 --- a/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java +++ b/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java @@ -9,7 +9,7 @@ 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 org.springframework.data.redis.core.RedisTemplate; import com.dnd.gongmuin.common.fixture.MemberFixture; import com.dnd.gongmuin.common.support.ApiTestSupport; @@ -20,16 +20,19 @@ import com.dnd.gongmuin.member.dto.request.ValidateNickNameRequest; import com.dnd.gongmuin.member.repository.MemberRepository; -@Transactional @DisplayName("[MemberController] 통합테스트") class MemberControllerTest extends ApiTestSupport { @Autowired private MemberRepository memberRepository; + @Autowired + private RedisTemplate redisTemplate; + @AfterEach void tearDown() { - memberRepository.deleteAll(); + redisTemplate.getConnectionFactory().getConnection().flushAll(); + } @DisplayName("닉네임 중복을 검증한다.") @@ -97,5 +100,4 @@ void reissue() throws Exception { .andExpect(status().isOk()) .andExpect(jsonPath("accessToken").isString()); } - } From 7e22abf5696ea3590872f722952616d151ee1a16 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 14:35:27 +0900 Subject: [PATCH 23/41] =?UTF-8?q?[feat]=20:=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83/=ED=86=A0=ED=81=B0=20=EC=9E=AC=EB=B0=9C=EA=B8=89=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20DTO=20Valid=20Message=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dnd/gongmuin/member/dto/request/LogoutRequest.java | 2 +- .../com/dnd/gongmuin/member/dto/request/ReissueRequest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 index fb065728..dda1ac4f 100644 --- a/src/main/java/com/dnd/gongmuin/member/dto/request/LogoutRequest.java +++ b/src/main/java/com/dnd/gongmuin/member/dto/request/LogoutRequest.java @@ -3,7 +3,7 @@ import jakarta.validation.constraints.NotEmpty; public record LogoutRequest( - @NotEmpty(message = "잘못된 요청입니다.") + @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 index 00f6b1b5..1275d413 100644 --- a/src/main/java/com/dnd/gongmuin/member/dto/request/ReissueRequest.java +++ b/src/main/java/com/dnd/gongmuin/member/dto/request/ReissueRequest.java @@ -3,7 +3,7 @@ import jakarta.validation.constraints.NotEmpty; public record ReissueRequest( - @NotEmpty(message = "잘못된 요청입니다.") + @NotEmpty(message = "AccessToken을 입력해주세요.") String accessToken ) { } From ff4d80d2cb3dd0f46a1c01d2f20b88e5af60e22d Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 14:36:23 +0900 Subject: [PATCH 24/41] =?UTF-8?q?[feat]=20:=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=97=90=EB=9F=AC=20=EC=BD=94=EB=93=9C=20message?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dnd/gongmuin/member/exception/MemberErrorCode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9d86cd4f..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,7 @@ public enum MemberErrorCode implements ErrorCode { NOT_FOUND_MEMBER("특정 회원을 찾을 수 없습니다.", "MEMBER_001"), NOT_FOUND_NEW_MEMBER("신규 회원이 아닙니다.", "MEMBER_002"), - FAIL_LOGOUT("로그아웃을 실패했습니다.", "MEMBER_003"), + LOGOUT_FAILED("로그아웃을 실패했습니다.", "MEMBER_003"), NOT_ENOUGH_CREDIT("보유한 크레딧이 부족합니다.", "MEMBER_004"); private final String message; From 129cd36e84687d697fbf6673a31f22310db5dee2 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 14:49:57 +0900 Subject: [PATCH 25/41] =?UTF-8?q?[feat]=20:=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=97=90=EB=9F=AC=20=EC=BD=94=EB=93=9C=20message?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dnd/gongmuin/member/service/MemberService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e0e115dc..66ff4117 100644 --- a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java +++ b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java @@ -134,7 +134,7 @@ public LogoutResponse logout(LogoutRequest request) { String values = redisUtil.getValues(accessToken); if (!Objects.equals(values, LOGOUT)) { - throw new NotFoundException(MemberErrorCode.FAIL_LOGOUT); + throw new NotFoundException(MemberErrorCode.LOGOUT_FAILED); } return new LogoutResponse(true); From 8f7733000b904aa520162c39fb54be5277237138 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 14:51:43 +0900 Subject: [PATCH 26/41] =?UTF-8?q?[style]=20:=20isOfficialEmailExists()=20?= =?UTF-8?q?=EC=9D=B8=EB=9D=BC=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dnd/gongmuin/member/service/MemberService.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 66ff4117..b1ec68ac 100644 --- a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java +++ b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java @@ -110,9 +110,7 @@ private Member updateAdditionalInfo(AdditionalInfoRequest request, Member findMe @Transactional(readOnly = true) public boolean isOfficialEmailExists(String officialEmail) { - boolean result = memberRepository.existsByOfficialEmail(officialEmail); - - return result; + return memberRepository.existsByOfficialEmail(officialEmail); } public LogoutResponse logout(LogoutRequest request) { From 3631a96b9b718d8513cdf2f009b07aea8808b55e Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 14:55:17 +0900 Subject: [PATCH 27/41] =?UTF-8?q?[refactor]=20:=20=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EB=B3=B8=EB=AC=B8=20=EB=82=B4=EC=9A=A9=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EC=84=A0=EC=96=B8=EC=9D=84=20=ED=86=B5=ED=95=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/dnd/gongmuin/mail/service/MailService.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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..611bb886 100644 --- a/src/main/java/com/dnd/gongmuin/mail/service/MailService.java +++ b/src/main/java/com/dnd/gongmuin/mail/service/MailService.java @@ -28,8 +28,9 @@ 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; @@ -71,7 +72,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) { From 2123399edc2a1541ff4705d3f9a1243394610470 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 15:00:54 +0900 Subject: [PATCH 28/41] =?UTF-8?q?[fix]=20:=20MailService=20<->=20MemberSer?= =?UTF-8?q?vice=20=EC=88=9C=ED=99=98=20=EC=B0=B8=EC=A1=B0=20=EC=9C=84?= =?UTF-8?q?=ED=97=98=EC=84=B1=20=EC=A0=9C=EA=B1=B0=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=EB=B3=80=EA=B2=BD=20-=20MailService=EA=B0=80=20Mem?= =?UTF-8?q?berService=EA=B0=80=20=EC=95=84=EB=8B=8C=20MemberRepository?= =?UTF-8?q?=EB=A5=BC=20=EC=9D=98=EC=A1=B4=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20-=20=EB=B3=80=EA=B2=BD=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B8=ED=95=9C=20checkDuplicatedOfficialEmail()=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20ReadOnly?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/dnd/gongmuin/mail/service/MailService.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 611bb886..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; @@ -35,7 +36,7 @@ public class MailService { 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(); @@ -87,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); } } From 0d1850227f05f2ea8295a3f810975cfbfe60570d Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 15:02:34 +0900 Subject: [PATCH 29/41] =?UTF-8?q?[fix]=20:=20throw=20=EB=88=84=EB=9D=BD=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dnd/gongmuin/member/service/MemberService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b1ec68ac..c0fb9472 100644 --- a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java +++ b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java @@ -84,7 +84,7 @@ public SignUpResponse signUp(AdditionalInfoRequest request, String email) { .orElseThrow(() -> new NotFoundException(MemberErrorCode.NOT_FOUND_MEMBER)); if (!isOfficialEmail(findMember)) { - new NotFoundException(MemberErrorCode.NOT_FOUND_NEW_MEMBER); + throw new NotFoundException(MemberErrorCode.NOT_FOUND_NEW_MEMBER); } updateAdditionalInfo(request, findMember); From 7f0ff1af8ab0d907620387a5e16d10d7cf6ccbab Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 15:05:03 +0900 Subject: [PATCH 30/41] =?UTF-8?q?[fix]=20:=20updateAdditionalInfo()=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EA=B0=90=EC=A7=80=EC=97=90=20=EC=9D=98?= =?UTF-8?q?=ED=95=9C=20save()=20=EB=A1=9C=EC=A7=81=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dnd/gongmuin/member/service/MemberService.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 c0fb9472..0177a137 100644 --- a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java +++ b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java @@ -97,15 +97,13 @@ 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) From 1da2cedbfdd8dabfa74cf5609953b935eb50966e Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 15:06:48 +0900 Subject: [PATCH 31/41] =?UTF-8?q?[style]=20:=20=EB=B0=98=ED=99=98=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=EB=AA=85=20=EC=88=98=EC=A0=95(findMember=20-?= =?UTF-8?q?>=20foundMember)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/dnd/gongmuin/member/service/MemberService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 0177a137..1172804c 100644 --- a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java +++ b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java @@ -80,16 +80,16 @@ public ValidateNickNameResponse isDuplicatedNickname(ValidateNickNameRequest req @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)) { + if (!isOfficialEmail(foundMember)) { throw new NotFoundException(MemberErrorCode.NOT_FOUND_NEW_MEMBER); } - updateAdditionalInfo(request, findMember); + updateAdditionalInfo(request, foundMember); - return new SignUpResponse(findMember.getNickname()); + return new SignUpResponse(foundMember.getNickname()); } public Member getMemberBySocialEmail(String socialEmail) { From 4fa58500ed3c91e40f29062d709adf2aac649024 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 15:21:16 +0900 Subject: [PATCH 32/41] =?UTF-8?q?[refactor]=20:=20Enum=20=EC=9E=AC?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=84=B1=EC=9D=84=20=EC=9C=84=ED=95=9C=20par?= =?UTF-8?q?seProviderFromSocialEmail()=20=EB=A1=9C=EC=A7=81=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/dnd/gongmuin/member/service/MemberService.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 1172804c..55a2db92 100644 --- a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java +++ b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java @@ -8,6 +8,7 @@ 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; @@ -54,10 +55,10 @@ public Member saveOrUpdate(Oauth2Response oauth2Response) { } public String parseProviderFromSocialEmail(Member member) { - String socialEmail = member.getSocialEmail().toUpperCase(); - if (socialEmail.contains("KAKAO")) { + String socialEmail = member.getSocialEmail(); + if (socialEmail.contains(Provider.KAKAO.getProvider())) { return "KAKAO"; - } else if (socialEmail.contains("NAVER")) { + } else if (socialEmail.contains(Provider.NAVER.getProvider())) { return "NAVER"; } throw new NotFoundException(AuthErrorCode.NOT_FOUND_PROVIDER); From 4ea9feddee591086fb998aa9a489bc5f0a702211 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 16:19:48 +0900 Subject: [PATCH 33/41] =?UTF-8?q?[test]=20:=20TokenProviderTest=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/jwt/TokenProviderTest.java | 52 +++++++++---------- 1 file changed, 24 insertions(+), 28 deletions(-) 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 46d8c528..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,26 +1,27 @@ 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; @@ -29,29 +30,30 @@ 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 AuthInfo authInfo; + private RedisUtil redisUtil; + + @Mock + 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분인 토큰이 생성된다.") @@ -61,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일인 토큰이 생성된다.") @@ -82,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 @@ -102,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("토큰의 만료일이 현재 시간보다 전이면 만료된 토큰이다.") @@ -122,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); From 8d729a12c191cca7dd30992053a2ec7be8a77c91 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 16:22:23 +0900 Subject: [PATCH 34/41] =?UTF-8?q?[rename]=20:=20isDuplicatedNickname()=20?= =?UTF-8?q?=EB=8B=89=EB=84=A4=EC=9E=84=20=EC=A1=B4=EC=9E=AC=20=EC=97=AC?= =?UTF-8?q?=EB=B6=80=20=EB=B3=80=EC=88=98=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dnd/gongmuin/member/service/MemberService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 55a2db92..8584236f 100644 --- a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java +++ b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java @@ -74,9 +74,9 @@ 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 From 0cc7d08ce8f961b79ccd916c563e963a9597c181 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 16:26:45 +0900 Subject: [PATCH 35/41] =?UTF-8?q?[test]=20:=20=EC=8B=A4=EC=A0=9C=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=ED=95=B4=EC=A7=84=20stub=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dnd/gongmuin/member/service/MemberServiceTest.java | 1 - 1 file changed, 1 deletion(-) 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 59f1f347..bfb5a1d2 100644 --- a/src/test/java/com/dnd/gongmuin/member/service/MemberServiceTest.java +++ b/src/test/java/com/dnd/gongmuin/member/service/MemberServiceTest.java @@ -100,7 +100,6 @@ void signUp() { Member member1 = createMember(null, "철수", "kakao123/kakao123@daum.net", null); given(memberRepository.findBySocialEmail("kakao123/kakao123@daum.net")).willReturn( Optional.ofNullable(member1)); - given(memberRepository.save(any(Member.class))).willReturn(member1); // when memberService.signUp(request, "kakao123/kakao123@daum.net"); From c69440b45668e4370a8f8a1061d97910e237582e Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 16:41:36 +0900 Subject: [PATCH 36/41] =?UTF-8?q?[test]=20:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20API=20=ED=86=B5=ED=95=A9=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EC=8B=A0=EA=B7=9C=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=20=EC=A0=80=EC=9E=A5=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberControllerTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java b/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java index 09fe4ecf..0c56c6c6 100644 --- a/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java +++ b/src/test/java/com/dnd/gongmuin/member/controller/MemberControllerTest.java @@ -5,6 +5,8 @@ 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; @@ -19,6 +21,9 @@ 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 { @@ -29,6 +34,9 @@ class MemberControllerTest extends ApiTestSupport { @Autowired private RedisTemplate redisTemplate; + @Autowired + private TokenProvider tokenProvider; + @AfterEach void tearDown() { redisTemplate.getConnectionFactory().getConnection().flushAll(); @@ -59,6 +67,12 @@ 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)) From cd5a73fe9ebf86934b5381847f7bbbfd51fabb71 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 16:44:31 +0900 Subject: [PATCH 37/41] =?UTF-8?q?[test]=20:=20=EC=8B=A4=EC=A0=9C=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD=EC=9C=BC=EB=A1=9C=20stu?= =?UTF-8?q?b=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20mock=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/dnd/gongmuin/mail/service/MailServiceTest.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) 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); From 1162d88656649a2f78cba503c15170fe1cd78daa Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 16:50:41 +0900 Subject: [PATCH 38/41] =?UTF-8?q?[test]:=20MemberFixture=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EC=83=9D=EC=84=B1=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/dnd/gongmuin/common/fixture/MemberFixture.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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( "김회원", From 04b04e07eadf172852b5687d9e1828654e040b84 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 19:17:53 +0900 Subject: [PATCH 39/41] =?UTF-8?q?[refactor]=20:=20=EC=86=8C=EC=85=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=A9=94=EC=9D=BC=EC=9D=84=20=ED=86=B5=ED=95=9C=20Pro?= =?UTF-8?q?vider=20=EC=B0=BE=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/dnd/gongmuin/auth/domain/Provider.java | 11 +++++++++-- .../com/dnd/gongmuin/auth/service/AuthService.java | 3 +-- .../dnd/gongmuin/member/service/MemberService.java | 9 ++------- 3 files changed, 12 insertions(+), 11 deletions(-) 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/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/member/service/MemberService.java b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java index 8584236f..1e995f9c 100644 --- a/src/main/java/com/dnd/gongmuin/member/service/MemberService.java +++ b/src/main/java/com/dnd/gongmuin/member/service/MemberService.java @@ -54,14 +54,9 @@ public Member saveOrUpdate(Oauth2Response oauth2Response) { return memberRepository.save(member); } - public String parseProviderFromSocialEmail(Member member) { + public Provider parseProviderFromSocialEmail(Member member) { String socialEmail = member.getSocialEmail(); - if (socialEmail.contains(Provider.KAKAO.getProvider())) { - return "KAKAO"; - } else if (socialEmail.contains(Provider.NAVER.getProvider())) { - return "NAVER"; - } - throw new NotFoundException(AuthErrorCode.NOT_FOUND_PROVIDER); + return Provider.fromSocialEmail(socialEmail); } private Member createMemberFromOauth2Response(Oauth2Response oauth2Response) { From 930152d5db1fe942855e3c53c697bf5e88280929 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 19:18:05 +0900 Subject: [PATCH 40/41] =?UTF-8?q?[test]=20:=20parseProviderFromSocialEmail?= =?UTF-8?q?()=20=EC=8B=A4=EC=A0=9C=20=EB=A1=9C=EC=A7=81=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20staticMock?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/MemberServiceTest.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) 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 bfb5a1d2..0c7f6000 100644 --- a/src/test/java/com/dnd/gongmuin/member/service/MemberServiceTest.java +++ b/src/test/java/com/dnd/gongmuin/member/service/MemberServiceTest.java @@ -14,10 +14,12 @@ 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; @@ -51,16 +53,21 @@ class MemberServiceTest { @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("공무원 이메일이 존재하는지 체크한다.") From d40766d4dee0afac062adfce497a1997a5e7fd05 Mon Sep 17 00:00:00 2001 From: dudxo Date: Sun, 11 Aug 2024 19:18:25 +0900 Subject: [PATCH 41/41] =?UTF-8?q?[test]=20:=20Provider=20Test=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gongmuin/auth/domain/ProviderTest.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/test/java/com/dnd/gongmuin/auth/domain/ProviderTest.java 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); + } + +}