From cdceaee062afad6d2724082f0232de84a0f71089 Mon Sep 17 00:00:00 2001 From: Sejin Park <95167215+sejineer@users.noreply.github.com> Date: Tue, 7 Nov 2023 00:05:09 +0900 Subject: [PATCH] =?UTF-8?q?[FEAT]:=20=EC=82=AC=EC=9E=A5=20Authorization=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#141)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 사장 로그아웃 기능 구현 * feat: 사장 탈퇴 기능 구현 * fix: 유저 로그인 유저 검증 추가 --- .../domain/auth/application/AuthService.java | 54 ++++++++++--------- .../auth/presentation/AuthController.java | 13 +++++ .../application/ShopOwnerService.java | 10 ++++ .../application/ShopOwnerServiceImpl.java | 23 ++++++++ .../presentation/ShopOwnerController.java | 13 +++++ 5 files changed, 87 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/shallwe/domain/auth/application/AuthService.java b/src/main/java/com/shallwe/domain/auth/application/AuthService.java index 441f001e..5b9808bf 100644 --- a/src/main/java/com/shallwe/domain/auth/application/AuthService.java +++ b/src/main/java/com/shallwe/domain/auth/application/AuthService.java @@ -1,6 +1,5 @@ package com.shallwe.domain.auth.application; -import java.net.URI; import java.util.Optional; import com.shallwe.domain.auth.dto.*; @@ -10,22 +9,19 @@ import com.shallwe.domain.shopowner.domain.repository.ShopOwnerRepository; import com.shallwe.domain.shopowner.exception.AlreadyExistPhoneNumberException; import com.shallwe.domain.shopowner.exception.InvalidPhoneNumberException; +import com.shallwe.domain.user.exception.InvalidUserException; import com.shallwe.global.DefaultAssert; -import com.shallwe.global.config.security.token.UserPrincipal; import com.shallwe.domain.user.domain.Provider; import com.shallwe.domain.user.domain.Role; import com.shallwe.domain.auth.domain.Token; import com.shallwe.domain.user.domain.User; import com.shallwe.global.error.DefaultAuthenticationException; -import com.shallwe.global.payload.ApiResponse; import com.shallwe.global.payload.ErrorCode; import com.shallwe.global.payload.Message; import com.shallwe.domain.auth.domain.repository.TokenRepository; import com.shallwe.domain.user.domain.repository.UserRepository; -import jakarta.validation.Valid; -import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -33,13 +29,13 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @Service +@Transactional(readOnly = true) public class AuthService { private final CustomTokenProviderService customTokenProviderService; @@ -91,6 +87,12 @@ public AuthRes signUp(final SignUpReq signUpReq) { @Transactional public AuthRes signIn(final SignInReq signInReq) { + User user = userRepository.findByEmail(signInReq.getEmail()) + .orElseThrow(InvalidUserException::new); + if (!user.getProviderId().equals(signInReq.getProviderId())) { + throw new InvalidPasswordException(); + } + Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( signInReq.getEmail(), @@ -113,6 +115,7 @@ public AuthRes signIn(final SignInReq signInReq) { .build(); } + @Transactional public AuthRes refresh(final RefreshTokenReq tokenRefreshRequest) { //1차 검증 boolean checkValid = valid(tokenRefreshRequest.getRefreshToken()); @@ -141,11 +144,8 @@ public AuthRes refresh(final RefreshTokenReq tokenRefreshRequest) { return authResponse; } + @Transactional public Message signOut(final RefreshTokenReq tokenRefreshRequest) { - boolean checkValid = valid(tokenRefreshRequest.getRefreshToken()); - DefaultAssert.isAuthentication(checkValid); - - //4 token 정보를 삭제한다. Token token = tokenRepository.findByRefreshToken(tokenRefreshRequest.getRefreshToken()) .orElseThrow(() -> new DefaultAuthenticationException(ErrorCode.INVALID_AUTHENTICATION)); tokenRepository.delete(token); @@ -155,22 +155,7 @@ public Message signOut(final RefreshTokenReq tokenRefreshRequest) { .build(); } - private boolean valid(final String refreshToken) { - //1. 토큰 형식 물리적 검증 - boolean validateCheck = customTokenProviderService.validateToken(refreshToken); - DefaultAssert.isTrue(validateCheck, "Token 검증에 실패하였습니다."); - - //2. refresh token 값을 불러온다. - Optional token = tokenRepository.findByRefreshToken(refreshToken); - DefaultAssert.isTrue(token.isPresent(), "탈퇴 처리된 회원입니다."); - - //3. email 값을 통해 인증값을 불러온다 - Authentication authentication = customTokenProviderService.getAuthenticationByEmail(token.get().getUserEmail()); - DefaultAssert.isTrue(token.get().getUserEmail().equals(authentication.getName()), "사용자 인증에 실패하였습니다."); - - return true; - } - + @Transactional public AuthRes shopOwnerSignUp(final ShopOwnerSignUpReq shopOwnerSignUpReq) { if (shopOwnerRepository.existsByPhoneNumber(shopOwnerSignUpReq.getPhoneNumber())) { throw new AlreadyExistPhoneNumberException(); @@ -208,6 +193,7 @@ public AuthRes shopOwnerSignUp(final ShopOwnerSignUpReq shopOwnerSignUpReq) { return authRes; } + @Transactional public AuthRes shopOwnerSignIn(final ShopOwnerSignInReq shopOwnerSignInReq) { ShopOwner shopOwner = shopOwnerRepository.findShopOwnerByPhoneNumber(shopOwnerSignInReq.getPhoneNumber()) .orElseThrow(InvalidPhoneNumberException::new); @@ -239,4 +225,20 @@ public AuthRes shopOwnerSignIn(final ShopOwnerSignInReq shopOwnerSignInReq) { return authRes; } + private boolean valid(final String refreshToken) { + //1. 토큰 형식 물리적 검증 + boolean validateCheck = customTokenProviderService.validateToken(refreshToken); + DefaultAssert.isTrue(validateCheck, "Token 검증에 실패하였습니다."); + + //2. refresh token 값을 불러온다. + Optional token = tokenRepository.findByRefreshToken(refreshToken); + DefaultAssert.isTrue(token.isPresent(), "탈퇴 처리된 회원입니다."); + + //3. email 값을 통해 인증값을 불러온다 + Authentication authentication = customTokenProviderService.getAuthenticationByEmail(token.get().getUserEmail()); + DefaultAssert.isTrue(token.get().getUserEmail().equals(authentication.getName()), "사용자 인증에 실패하였습니다."); + + return true; + } + } \ No newline at end of file diff --git a/src/main/java/com/shallwe/domain/auth/presentation/AuthController.java b/src/main/java/com/shallwe/domain/auth/presentation/AuthController.java index 34c04120..34714717 100644 --- a/src/main/java/com/shallwe/domain/auth/presentation/AuthController.java +++ b/src/main/java/com/shallwe/domain/auth/presentation/AuthController.java @@ -138,4 +138,17 @@ public ResponseCustom shopOwnerSignIn( return ResponseCustom.OK(authService.shopOwnerSignIn(shopOwnerSignInReq)); } + @Operation(summary = "사장 로그아웃", description = "사장 로그아웃을 수행합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "사장 로그아웃 성공", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = Message.class) ) } ), + @ApiResponse(responseCode = "400", description = "사장 로그아웃 실패", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class) ) } ), + }) + @PostMapping(value="/shop-owner/sign-out") + public ResponseCustom shopOwnerSignOut( + @Parameter(description = "Accesstoken을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal, + @Parameter(description = "Schemas의 RefreshTokenRequest를 참고해주세요.", required = true) @Valid @RequestBody RefreshTokenReq tokenRefreshRequest + ) { + return ResponseCustom.OK(authService.signOut(tokenRefreshRequest)); + } + } diff --git a/src/main/java/com/shallwe/domain/shopowner/application/ShopOwnerService.java b/src/main/java/com/shallwe/domain/shopowner/application/ShopOwnerService.java index c797f8d9..f8316ac0 100644 --- a/src/main/java/com/shallwe/domain/shopowner/application/ShopOwnerService.java +++ b/src/main/java/com/shallwe/domain/shopowner/application/ShopOwnerService.java @@ -1,4 +1,14 @@ package com.shallwe.domain.shopowner.application; +import com.shallwe.domain.shopowner.dto.ShopOwnerChangePasswordReq; +import com.shallwe.domain.user.dto.DeleteUserRes; +import com.shallwe.global.config.security.token.UserPrincipal; +import com.shallwe.global.payload.Message; + public interface ShopOwnerService { + + Message shopOwnerChangePassword(UserPrincipal userPrincipal, ShopOwnerChangePasswordReq shopOwnerChangePasswordReq); + Message deleteCurrentShopOwner(UserPrincipal userPrincipal); + } + diff --git a/src/main/java/com/shallwe/domain/shopowner/application/ShopOwnerServiceImpl.java b/src/main/java/com/shallwe/domain/shopowner/application/ShopOwnerServiceImpl.java index 69d3eab0..b09821e3 100644 --- a/src/main/java/com/shallwe/domain/shopowner/application/ShopOwnerServiceImpl.java +++ b/src/main/java/com/shallwe/domain/shopowner/application/ShopOwnerServiceImpl.java @@ -1,10 +1,15 @@ package com.shallwe.domain.shopowner.application; +import com.shallwe.domain.auth.domain.Token; +import com.shallwe.domain.auth.domain.repository.TokenRepository; +import com.shallwe.domain.common.Status; import com.shallwe.domain.shopowner.domain.ShopOwner; import com.shallwe.domain.shopowner.domain.repository.ShopOwnerRepository; import com.shallwe.domain.shopowner.dto.ShopOwnerChangePasswordReq; import com.shallwe.domain.shopowner.exception.InvalidShopOwnerException; +import com.shallwe.domain.user.domain.User; +import com.shallwe.domain.user.exception.InvalidTokenException; import com.shallwe.global.config.security.token.UserPrincipal; import com.shallwe.global.payload.Message; import lombok.RequiredArgsConstructor; @@ -19,7 +24,9 @@ public class ShopOwnerServiceImpl implements ShopOwnerService { private final PasswordEncoder passwordEncoder; private final ShopOwnerRepository shopOwnerRepository; + private final TokenRepository tokenRepository; + @Override @Transactional public Message shopOwnerChangePassword(final UserPrincipal userPrincipal, final ShopOwnerChangePasswordReq shopOwnerChangePasswordReq) { ShopOwner shopOwner = shopOwnerRepository.findById(userPrincipal.getId()) @@ -31,4 +38,20 @@ public Message shopOwnerChangePassword(final UserPrincipal userPrincipal, final .message("비밀번호가 변경되었습니다.").build(); } + @Override + @Transactional + public Message deleteCurrentShopOwner(UserPrincipal userPrincipal) { + ShopOwner shopOwner = shopOwnerRepository.findById(userPrincipal.getId()) + .orElseThrow(InvalidShopOwnerException::new); + Token token = tokenRepository.findByUserEmail(userPrincipal.getEmail()) + .orElseThrow(InvalidTokenException::new); + + shopOwner.updateStatus(Status.DELETE); + tokenRepository.delete(token); + + return Message.builder() + .message("사장 탈퇴가 완료되었습니다.") + .build(); + } + } diff --git a/src/main/java/com/shallwe/domain/shopowner/presentation/ShopOwnerController.java b/src/main/java/com/shallwe/domain/shopowner/presentation/ShopOwnerController.java index bc7c9c8e..f60d0b31 100644 --- a/src/main/java/com/shallwe/domain/shopowner/presentation/ShopOwnerController.java +++ b/src/main/java/com/shallwe/domain/shopowner/presentation/ShopOwnerController.java @@ -3,6 +3,7 @@ import com.shallwe.domain.shopowner.application.ShopOwnerServiceImpl; import com.shallwe.domain.shopowner.dto.ShopOwnerChangePasswordReq; +import com.shallwe.domain.user.dto.DeleteUserRes; import com.shallwe.global.config.security.token.CurrentUser; import com.shallwe.global.config.security.token.UserPrincipal; import com.shallwe.global.payload.ErrorResponse; @@ -39,4 +40,16 @@ public ResponseCustom shopOwnerChangePassword( return ResponseCustom.OK(shopOwnerService.shopOwnerChangePassword(userPrincipal, shopOwnerChangePasswordReq)); } + @Operation(summary = "사장 탈퇴", description = "사장 탈퇴를 수행합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "사장 탈퇴 성공", content = {@Content(mediaType = "application/json", schema = @Schema(implementation = Message.class))}), + @ApiResponse(responseCode = "400", description = "사장 탈퇴 실패", content = {@Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class))}), + }) + @PatchMapping + public ResponseCustom deleteCurrentShopOwner( + @Parameter(description = "AccessToken 을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal + ) { + return ResponseCustom.OK(shopOwnerService.deleteCurrentShopOwner(userPrincipal)); + } + }