From 8f3ff3dcbc0de09b63b639ff8dfa0ab0e047d16b Mon Sep 17 00:00:00 2001 From: min-0 Date: Wed, 4 Sep 2024 02:45:22 +0900 Subject: [PATCH 1/5] =?UTF-8?q?=E2=9C=A8=20=ED=9A=8C=EC=9B=90=20=ED=83=88?= =?UTF-8?q?=ED=87=B4=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 애플 소셜로그인 탈퇴, 서버 내 유저 정보 삭제 --- .../dndtravel/auth/domain/QRefreshToken.java | 43 +++++++++++++ .../dnd/dndtravel/map/domain/QAttraction.java | 53 ++++++++++++++++ .../map/domain/QMemberAttraction.java | 60 +++++++++++++++++++ .../dndtravel/map/domain/QMemberRegion.java | 56 +++++++++++++++++ .../com/dnd/dndtravel/map/domain/QPhoto.java | 53 ++++++++++++++++ .../com/dnd/dndtravel/map/domain/QRegion.java | 39 ++++++++++++ .../dnd/dndtravel/member/domain/QMember.java | 43 +++++++++++++ .../auth/controller/AuthController.java | 18 ++++++ .../request/AppleRevokeRequest.java | 18 ++++++ .../request/AppleWithdrawRequest.java | 10 ++++ .../dndtravel/auth/service/AppleClient.java | 29 +++++---- .../auth/service/AppleOAuthService.java | 33 +++++++++- .../member/service/MemberService.java | 8 +++ 13 files changed, 452 insertions(+), 11 deletions(-) create mode 100644 src/main/generated/com/dnd/dndtravel/auth/domain/QRefreshToken.java create mode 100644 src/main/generated/com/dnd/dndtravel/map/domain/QAttraction.java create mode 100644 src/main/generated/com/dnd/dndtravel/map/domain/QMemberAttraction.java create mode 100644 src/main/generated/com/dnd/dndtravel/map/domain/QMemberRegion.java create mode 100644 src/main/generated/com/dnd/dndtravel/map/domain/QPhoto.java create mode 100644 src/main/generated/com/dnd/dndtravel/map/domain/QRegion.java create mode 100644 src/main/generated/com/dnd/dndtravel/member/domain/QMember.java create mode 100644 src/main/java/com/dnd/dndtravel/auth/controller/request/AppleRevokeRequest.java create mode 100644 src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java diff --git a/src/main/generated/com/dnd/dndtravel/auth/domain/QRefreshToken.java b/src/main/generated/com/dnd/dndtravel/auth/domain/QRefreshToken.java new file mode 100644 index 0000000..390ac71 --- /dev/null +++ b/src/main/generated/com/dnd/dndtravel/auth/domain/QRefreshToken.java @@ -0,0 +1,43 @@ +package com.dnd.dndtravel.auth.domain; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; + + +/** + * QRefreshToken is a Querydsl query type for RefreshToken + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QRefreshToken extends EntityPathBase { + + private static final long serialVersionUID = 1653736957L; + + public static final QRefreshToken refreshToken1 = new QRefreshToken("refreshToken1"); + + public final DateTimePath expiredTime = createDateTime("expiredTime", java.time.LocalDateTime.class); + + public final NumberPath id = createNumber("id", Long.class); + + public final NumberPath memberId = createNumber("memberId", Long.class); + + public final StringPath refreshToken = createString("refreshToken"); + + public QRefreshToken(String variable) { + super(RefreshToken.class, forVariable(variable)); + } + + public QRefreshToken(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QRefreshToken(PathMetadata metadata) { + super(RefreshToken.class, metadata); + } + +} + diff --git a/src/main/generated/com/dnd/dndtravel/map/domain/QAttraction.java b/src/main/generated/com/dnd/dndtravel/map/domain/QAttraction.java new file mode 100644 index 0000000..9fd1bdf --- /dev/null +++ b/src/main/generated/com/dnd/dndtravel/map/domain/QAttraction.java @@ -0,0 +1,53 @@ +package com.dnd.dndtravel.map.domain; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QAttraction is a Querydsl query type for Attraction + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QAttraction extends EntityPathBase { + + private static final long serialVersionUID = 7154276L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QAttraction attraction = new QAttraction("attraction"); + + public final NumberPath id = createNumber("id", Long.class); + + public final StringPath name = createString("name"); + + public final QRegion region; + + public QAttraction(String variable) { + this(Attraction.class, forVariable(variable), INITS); + } + + public QAttraction(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QAttraction(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QAttraction(PathMetadata metadata, PathInits inits) { + this(Attraction.class, metadata, inits); + } + + public QAttraction(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.region = inits.isInitialized("region") ? new QRegion(forProperty("region")) : null; + } + +} + diff --git a/src/main/generated/com/dnd/dndtravel/map/domain/QMemberAttraction.java b/src/main/generated/com/dnd/dndtravel/map/domain/QMemberAttraction.java new file mode 100644 index 0000000..10876fe --- /dev/null +++ b/src/main/generated/com/dnd/dndtravel/map/domain/QMemberAttraction.java @@ -0,0 +1,60 @@ +package com.dnd.dndtravel.map.domain; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QMemberAttraction is a Querydsl query type for MemberAttraction + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QMemberAttraction extends EntityPathBase { + + private static final long serialVersionUID = -155605282L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QMemberAttraction memberAttraction = new QMemberAttraction("memberAttraction"); + + public final QAttraction attraction; + + public final NumberPath id = createNumber("id", Long.class); + + public final DatePath localDate = createDate("localDate", java.time.LocalDate.class); + + public final com.dnd.dndtravel.member.domain.QMember member; + + public final StringPath memo = createString("memo"); + + public final StringPath region = createString("region"); + + public QMemberAttraction(String variable) { + this(MemberAttraction.class, forVariable(variable), INITS); + } + + public QMemberAttraction(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QMemberAttraction(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QMemberAttraction(PathMetadata metadata, PathInits inits) { + this(MemberAttraction.class, metadata, inits); + } + + public QMemberAttraction(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.attraction = inits.isInitialized("attraction") ? new QAttraction(forProperty("attraction"), inits.get("attraction")) : null; + this.member = inits.isInitialized("member") ? new com.dnd.dndtravel.member.domain.QMember(forProperty("member")) : null; + } + +} + diff --git a/src/main/generated/com/dnd/dndtravel/map/domain/QMemberRegion.java b/src/main/generated/com/dnd/dndtravel/map/domain/QMemberRegion.java new file mode 100644 index 0000000..475afd6 --- /dev/null +++ b/src/main/generated/com/dnd/dndtravel/map/domain/QMemberRegion.java @@ -0,0 +1,56 @@ +package com.dnd.dndtravel.map.domain; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QMemberRegion is a Querydsl query type for MemberRegion + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QMemberRegion extends EntityPathBase { + + private static final long serialVersionUID = -919485973L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QMemberRegion memberRegion = new QMemberRegion("memberRegion"); + + public final NumberPath id = createNumber("id", Long.class); + + public final com.dnd.dndtravel.member.domain.QMember member; + + public final QRegion region; + + public final NumberPath visitCount = createNumber("visitCount", Integer.class); + + public QMemberRegion(String variable) { + this(MemberRegion.class, forVariable(variable), INITS); + } + + public QMemberRegion(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QMemberRegion(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QMemberRegion(PathMetadata metadata, PathInits inits) { + this(MemberRegion.class, metadata, inits); + } + + public QMemberRegion(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.member = inits.isInitialized("member") ? new com.dnd.dndtravel.member.domain.QMember(forProperty("member")) : null; + this.region = inits.isInitialized("region") ? new QRegion(forProperty("region")) : null; + } + +} + diff --git a/src/main/generated/com/dnd/dndtravel/map/domain/QPhoto.java b/src/main/generated/com/dnd/dndtravel/map/domain/QPhoto.java new file mode 100644 index 0000000..e415999 --- /dev/null +++ b/src/main/generated/com/dnd/dndtravel/map/domain/QPhoto.java @@ -0,0 +1,53 @@ +package com.dnd.dndtravel.map.domain; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QPhoto is a Querydsl query type for Photo + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QPhoto extends EntityPathBase { + + private static final long serialVersionUID = 440715541L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QPhoto photo = new QPhoto("photo"); + + public final NumberPath id = createNumber("id", Long.class); + + public final QMemberAttraction memberAttraction; + + public final StringPath url = createString("url"); + + public QPhoto(String variable) { + this(Photo.class, forVariable(variable), INITS); + } + + public QPhoto(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QPhoto(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QPhoto(PathMetadata metadata, PathInits inits) { + this(Photo.class, metadata, inits); + } + + public QPhoto(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.memberAttraction = inits.isInitialized("memberAttraction") ? new QMemberAttraction(forProperty("memberAttraction"), inits.get("memberAttraction")) : null; + } + +} + diff --git a/src/main/generated/com/dnd/dndtravel/map/domain/QRegion.java b/src/main/generated/com/dnd/dndtravel/map/domain/QRegion.java new file mode 100644 index 0000000..179ca32 --- /dev/null +++ b/src/main/generated/com/dnd/dndtravel/map/domain/QRegion.java @@ -0,0 +1,39 @@ +package com.dnd.dndtravel.map.domain; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; + + +/** + * QRegion is a Querydsl query type for Region + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QRegion extends EntityPathBase { + + private static final long serialVersionUID = 831518833L; + + public static final QRegion region = new QRegion("region"); + + public final NumberPath id = createNumber("id", Long.class); + + public final StringPath name = createString("name"); + + public QRegion(String variable) { + super(Region.class, forVariable(variable)); + } + + public QRegion(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QRegion(PathMetadata metadata) { + super(Region.class, metadata); + } + +} + diff --git a/src/main/generated/com/dnd/dndtravel/member/domain/QMember.java b/src/main/generated/com/dnd/dndtravel/member/domain/QMember.java new file mode 100644 index 0000000..faf3118 --- /dev/null +++ b/src/main/generated/com/dnd/dndtravel/member/domain/QMember.java @@ -0,0 +1,43 @@ +package com.dnd.dndtravel.member.domain; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; + + +/** + * QMember is a Querydsl query type for Member + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QMember extends EntityPathBase { + + private static final long serialVersionUID = 1458466475L; + + public static final QMember member = new QMember("member1"); + + public final StringPath email = createString("email"); + + public final NumberPath id = createNumber("id", Long.class); + + public final StringPath name = createString("name"); + + public final EnumPath selectedColor = createEnum("selectedColor", SelectedColor.class); + + public QMember(String variable) { + super(Member.class, forVariable(variable)); + } + + public QMember(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QMember(PathMetadata metadata) { + super(Member.class, metadata); + } + +} + diff --git a/src/main/java/com/dnd/dndtravel/auth/controller/AuthController.java b/src/main/java/com/dnd/dndtravel/auth/controller/AuthController.java index 99dd550..71e623b 100644 --- a/src/main/java/com/dnd/dndtravel/auth/controller/AuthController.java +++ b/src/main/java/com/dnd/dndtravel/auth/controller/AuthController.java @@ -1,5 +1,6 @@ package com.dnd.dndtravel.auth.controller; +import com.dnd.dndtravel.auth.controller.request.AppleWithdrawRequest; import com.dnd.dndtravel.auth.controller.request.ReIssueTokenRequest; import com.dnd.dndtravel.auth.service.dto.response.AppleIdTokenPayload; import com.dnd.dndtravel.auth.service.AppleOAuthService; @@ -48,4 +49,21 @@ public ResponseEntity appleOAuthLogin(@RequestBody AppleLoginRequ public ReissueTokenResponse reissueToken(@RequestBody ReIssueTokenRequest reissueTokenRequest) { return jwtTokenService.reIssue(reissueTokenRequest.refreshToken()); } + + @PostMapping("/withdraw") + public ResponseEntity withdraw(@RequestBody AppleWithdrawRequest withdrawRequest) { + // 1. Apple 서버에서 Access Token 받아오기 + String accessToken = appleOAuthService.getAccessToken(withdrawRequest.authorizationCode()); + + // 2. Apple 서버에 탈퇴 요청 + boolean revokeSuccessful = appleOAuthService.revoke(accessToken); + + if (revokeSuccessful) { + // 3. 자체 회원 탈퇴 진행 + memberService.withdrawMember(withdrawRequest.memberId()); + return ResponseEntity.ok().build(); + } else { + return ResponseEntity.badRequest().build(); + } + } } \ No newline at end of file diff --git a/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleRevokeRequest.java b/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleRevokeRequest.java new file mode 100644 index 0000000..3fdf546 --- /dev/null +++ b/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleRevokeRequest.java @@ -0,0 +1,18 @@ +package com.dnd.dndtravel.auth.controller.request; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/* +서버에서 Apple 서버로 보내는 요청 + */ +public record AppleRevokeRequest( + @JsonProperty("client_id") + String clientId, + @JsonProperty("client_secret") + String clientSecret, + @JsonProperty("token") + String Token, + @JsonProperty("token_type_hint") + String tokenType +) { +} diff --git a/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java b/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java new file mode 100644 index 0000000..8adae83 --- /dev/null +++ b/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java @@ -0,0 +1,10 @@ +package com.dnd.dndtravel.auth.controller.request; + +/* +클라이언트에서 서버로 보내는 요청 + */ +public record AppleWithdrawRequest( + String authorizationCode, + long memberId +) { +} diff --git a/src/main/java/com/dnd/dndtravel/auth/service/AppleClient.java b/src/main/java/com/dnd/dndtravel/auth/service/AppleClient.java index fc31c33..e365165 100644 --- a/src/main/java/com/dnd/dndtravel/auth/service/AppleClient.java +++ b/src/main/java/com/dnd/dndtravel/auth/service/AppleClient.java @@ -1,5 +1,6 @@ package com.dnd.dndtravel.auth.service; +import com.dnd.dndtravel.auth.controller.request.AppleRevokeRequest; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -8,23 +9,31 @@ import com.dnd.dndtravel.auth.config.AppleFeignClientConfiguration; @FeignClient( - name = "apple-auth", - url = "https://appleid.apple.com", - configuration = AppleFeignClientConfiguration.class + name = "apple-auth", + url = "https://appleid.apple.com", + configuration = AppleFeignClientConfiguration.class ) public interface AppleClient { /** - * @param clientId(required) : 맵땅의 식별자값 + * @param clientId(required) : 맵땅의 식별자값 * @param clientSecret(required) : 개발자가 만든 비밀 JWT 토큰, 개발자 계정과 비밀키로 애플에 로그인할때 사용된다. - * @param grantType(required) : refresh token과 authorization code 를 검증하기위해 사용됨, 우린 현재 authorization code 사용중 - * @param code : 애플에게 받은 오직 5분만 유효한 일회용 인증코드, authorization code 검증 용도로 필요하다. + * @param grantType(required) : refresh token과 authorization code 를 검증하기위해 사용됨, 우린 현재 authorization code 사용중 + * @param code : 애플에게 받은 오직 5분만 유효한 일회용 인증코드, authorization code 검증 용도로 필요하다. * @return */ @PostMapping("/auth/token") AppleSocialTokenInfoResponse getIdToken( - @RequestParam("client_id") String clientId, - @RequestParam("client_secret") String clientSecret, - @RequestParam("grant_type") String grantType, - @RequestParam("code") String code + @RequestParam("client_id") String clientId, + @RequestParam("client_secret") String clientSecret, + @RequestParam("grant_type") String grantType, + @RequestParam("code") String code + ); + + @PostMapping("/auth/revoke") + AppleRevokeRequest revoke( + @RequestParam("client_id") String clientId, + @RequestParam("client_secret") String clientSecret, + @RequestParam("token") String refreshToken, + @RequestParam("token_type_hint") String tokenType ); } diff --git a/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java b/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java index 8145dd5..7e17a23 100644 --- a/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java +++ b/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java @@ -1,5 +1,7 @@ package com.dnd.dndtravel.auth.service; +import com.dnd.dndtravel.auth.controller.request.AppleRevokeRequest; +import com.dnd.dndtravel.auth.service.dto.response.AppleSocialTokenInfoResponse; import io.jsonwebtoken.JwsHeader; import io.jsonwebtoken.Jwts; import lombok.RequiredArgsConstructor; @@ -38,7 +40,6 @@ * "aud": "https://appleid.apple.com", * "sub": "com.mytest.app" * } - * */ @RequiredArgsConstructor @@ -86,4 +87,34 @@ private PrivateKey getPrivateKey() { throw new RuntimeException("Error converting private key from String", e); } } + + public String getAccessToken(String authorizationCode) { + AppleSocialTokenInfoResponse tokenInfo = appleClient.getIdToken( + appleProperties.getClientId(), + generateClientSecret(), + appleProperties.getGrantType(), + authorizationCode + ); + return tokenInfo.accessToken(); + } + + public boolean revoke(String accessToken) { + try { + AppleRevokeRequest revokeRequest = new AppleRevokeRequest( + appleProperties.getClientId(), + generateClientSecret(), + accessToken, + "access_token" + ); + appleClient.revoke( + revokeRequest.clientId(), + revokeRequest.clientSecret(), + revokeRequest.Token(), + revokeRequest.tokenType() + ); + return true; + } catch (Exception e) { + throw new RuntimeException("Error revoking Apple token", e); + } + } } diff --git a/src/main/java/com/dnd/dndtravel/member/service/MemberService.java b/src/main/java/com/dnd/dndtravel/member/service/MemberService.java index 973a345..a03004a 100644 --- a/src/main/java/com/dnd/dndtravel/member/service/MemberService.java +++ b/src/main/java/com/dnd/dndtravel/member/service/MemberService.java @@ -17,4 +17,12 @@ public Member saveMember(String name, String email, String selectedColor) { return memberRepository.findByEmail(email) .orElseGet(() -> memberRepository.save(Member.of(name, email,selectedColor))); } + + @Transactional + public void withdrawMember(long memberId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new RuntimeException("Member not found")); + + memberRepository.delete(member); + } } From 566698a110291f99b82de6d18475f5049bcdec35 Mon Sep 17 00:00:00 2001 From: min-0 Date: Wed, 4 Sep 2024 02:56:47 +0900 Subject: [PATCH 2/5] =?UTF-8?q?:bug:=20=EC=98=88=EC=99=B8=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=EC=8B=9C=20false=20=EB=B0=98=ED=99=98=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=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/dndtravel/auth/service/AppleOAuthService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java b/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java index 7e17a23..f6e19b1 100644 --- a/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java +++ b/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java @@ -114,7 +114,8 @@ public boolean revoke(String accessToken) { ); return true; } catch (Exception e) { - throw new RuntimeException("Error revoking Apple token", e); + log.error("Error revoking Apple token", e); + return false; } } } From 38373cd39867ecb6d368c6b4965d20bc76c30ed4 Mon Sep 17 00:00:00 2001 From: min-0 Date: Wed, 4 Sep 2024 17:39:23 +0900 Subject: [PATCH 3/5] =?UTF-8?q?:sparkles:=20Member=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=82=AD=EC=A0=9C=20=EC=B6=94=EA=B0=80(?= =?UTF-8?q?=EB=AA=85=EC=86=8C,=20=EC=A7=80=EC=97=AD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/dnd/dndtravel/member/service/MemberService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/dnd/dndtravel/member/service/MemberService.java b/src/main/java/com/dnd/dndtravel/member/service/MemberService.java index a03004a..986d55e 100644 --- a/src/main/java/com/dnd/dndtravel/member/service/MemberService.java +++ b/src/main/java/com/dnd/dndtravel/member/service/MemberService.java @@ -1,5 +1,7 @@ package com.dnd.dndtravel.member.service; +import com.dnd.dndtravel.map.repository.MemberAttractionRepository; +import com.dnd.dndtravel.map.repository.MemberRegionRepository; import com.dnd.dndtravel.member.domain.Member; import com.dnd.dndtravel.member.repository.MemberRepository; import lombok.RequiredArgsConstructor; @@ -11,6 +13,8 @@ public class MemberService { private final MemberRepository memberRepository; + private final MemberAttractionRepository memberAttractionRepository; + private final MemberRegionRepository memberRegionRepository; @Transactional public Member saveMember(String name, String email, String selectedColor) { @@ -24,5 +28,7 @@ public void withdrawMember(long memberId) { .orElseThrow(() -> new RuntimeException("Member not found")); memberRepository.delete(member); + memberAttractionRepository.deleteById(memberId); + memberRegionRepository.deleteById(memberId); } } From 4d49fee671cc5678d1952fd6f179789c29ccbe2a Mon Sep 17 00:00:00 2001 From: min-0 Date: Thu, 5 Sep 2024 02:03:17 +0900 Subject: [PATCH 4/5] =?UTF-8?q?:recycle:=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=EB=A5=BC=20=EB=B0=98=EC=98=81=ED=95=9C=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=20=ED=83=88=ED=87=B4=20api=20=EA=B4=80=EB=A0=A8=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 --- .../auth/controller/AuthController.java | 21 +++++++------------ .../request/AppleWithdrawRequest.java | 8 +++++-- .../dndtravel/auth/service/AppleClient.java | 4 ++-- .../auth/service/AppleOAuthService.java | 15 +++---------- .../dto/response/AppleRevokeResponse.java} | 9 +++----- .../member/service/MemberService.java | 3 +++ 6 files changed, 25 insertions(+), 35 deletions(-) rename src/main/java/com/dnd/dndtravel/auth/{controller/request/AppleRevokeRequest.java => service/dto/response/AppleRevokeResponse.java} (64%) diff --git a/src/main/java/com/dnd/dndtravel/auth/controller/AuthController.java b/src/main/java/com/dnd/dndtravel/auth/controller/AuthController.java index 71e623b..53c440a 100644 --- a/src/main/java/com/dnd/dndtravel/auth/controller/AuthController.java +++ b/src/main/java/com/dnd/dndtravel/auth/controller/AuthController.java @@ -8,15 +8,15 @@ import com.dnd.dndtravel.auth.controller.request.AppleLoginRequest; import com.dnd.dndtravel.auth.service.dto.response.TokenResponse; import com.dnd.dndtravel.auth.service.dto.response.ReissueTokenResponse; +import com.dnd.dndtravel.config.AuthenticationMember; import com.dnd.dndtravel.member.domain.Member; import com.dnd.dndtravel.member.service.MemberService; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RequiredArgsConstructor @RestController @@ -50,20 +50,15 @@ public ReissueTokenResponse reissueToken(@RequestBody ReIssueTokenRequest reissu return jwtTokenService.reIssue(reissueTokenRequest.refreshToken()); } - @PostMapping("/withdraw") - public ResponseEntity withdraw(@RequestBody AppleWithdrawRequest withdrawRequest) { + @DeleteMapping("/withdraw") + public void withdraw(@Valid @RequestBody AppleWithdrawRequest withdrawRequest, AuthenticationMember authenticationMember) { // 1. Apple 서버에서 Access Token 받아오기 String accessToken = appleOAuthService.getAccessToken(withdrawRequest.authorizationCode()); // 2. Apple 서버에 탈퇴 요청 - boolean revokeSuccessful = appleOAuthService.revoke(accessToken); + appleOAuthService.revoke(accessToken); - if (revokeSuccessful) { - // 3. 자체 회원 탈퇴 진행 - memberService.withdrawMember(withdrawRequest.memberId()); - return ResponseEntity.ok().build(); - } else { - return ResponseEntity.badRequest().build(); - } + // 3. 자체 회원 탈퇴 진행 + memberService.withdrawMember(authenticationMember.id()); } } \ No newline at end of file diff --git a/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java b/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java index 8adae83..0ba39a7 100644 --- a/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java +++ b/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java @@ -1,10 +1,14 @@ package com.dnd.dndtravel.auth.controller.request; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + /* 클라이언트에서 서버로 보내는 요청 */ public record AppleWithdrawRequest( - String authorizationCode, - long memberId + @NotBlank(message = "인증 코드가 존재하지 않습니다.") + @Size(max = 12, message = "인증 코드가 12자를 초과하였습니다.") + String authorizationCode ) { } diff --git a/src/main/java/com/dnd/dndtravel/auth/service/AppleClient.java b/src/main/java/com/dnd/dndtravel/auth/service/AppleClient.java index e365165..e13b234 100644 --- a/src/main/java/com/dnd/dndtravel/auth/service/AppleClient.java +++ b/src/main/java/com/dnd/dndtravel/auth/service/AppleClient.java @@ -1,6 +1,6 @@ package com.dnd.dndtravel.auth.service; -import com.dnd.dndtravel.auth.controller.request.AppleRevokeRequest; +import com.dnd.dndtravel.auth.service.dto.response.AppleRevokeResponse; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -30,7 +30,7 @@ AppleSocialTokenInfoResponse getIdToken( ); @PostMapping("/auth/revoke") - AppleRevokeRequest revoke( + AppleRevokeResponse revoke( @RequestParam("client_id") String clientId, @RequestParam("client_secret") String clientSecret, @RequestParam("token") String refreshToken, diff --git a/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java b/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java index f6e19b1..1e9fbe9 100644 --- a/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java +++ b/src/main/java/com/dnd/dndtravel/auth/service/AppleOAuthService.java @@ -1,6 +1,5 @@ package com.dnd.dndtravel.auth.service; -import com.dnd.dndtravel.auth.controller.request.AppleRevokeRequest; import com.dnd.dndtravel.auth.service.dto.response.AppleSocialTokenInfoResponse; import io.jsonwebtoken.JwsHeader; import io.jsonwebtoken.Jwts; @@ -98,24 +97,16 @@ public String getAccessToken(String authorizationCode) { return tokenInfo.accessToken(); } - public boolean revoke(String accessToken) { + public void revoke(String accessToken) { try { - AppleRevokeRequest revokeRequest = new AppleRevokeRequest( + appleClient.revoke( appleProperties.getClientId(), generateClientSecret(), accessToken, "access_token" ); - appleClient.revoke( - revokeRequest.clientId(), - revokeRequest.clientSecret(), - revokeRequest.Token(), - revokeRequest.tokenType() - ); - return true; } catch (Exception e) { - log.error("Error revoking Apple token", e); - return false; + throw new RuntimeException("Error revoking Apple token", e); } } } diff --git a/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleRevokeRequest.java b/src/main/java/com/dnd/dndtravel/auth/service/dto/response/AppleRevokeResponse.java similarity index 64% rename from src/main/java/com/dnd/dndtravel/auth/controller/request/AppleRevokeRequest.java rename to src/main/java/com/dnd/dndtravel/auth/service/dto/response/AppleRevokeResponse.java index 3fdf546..488ceb3 100644 --- a/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleRevokeRequest.java +++ b/src/main/java/com/dnd/dndtravel/auth/service/dto/response/AppleRevokeResponse.java @@ -1,17 +1,14 @@ -package com.dnd.dndtravel.auth.controller.request; +package com.dnd.dndtravel.auth.service.dto.response; import com.fasterxml.jackson.annotation.JsonProperty; -/* -서버에서 Apple 서버로 보내는 요청 - */ -public record AppleRevokeRequest( +public record AppleRevokeResponse( @JsonProperty("client_id") String clientId, @JsonProperty("client_secret") String clientSecret, @JsonProperty("token") - String Token, + String token, @JsonProperty("token_type_hint") String tokenType ) { diff --git a/src/main/java/com/dnd/dndtravel/member/service/MemberService.java b/src/main/java/com/dnd/dndtravel/member/service/MemberService.java index 986d55e..6b18278 100644 --- a/src/main/java/com/dnd/dndtravel/member/service/MemberService.java +++ b/src/main/java/com/dnd/dndtravel/member/service/MemberService.java @@ -1,5 +1,6 @@ package com.dnd.dndtravel.member.service; +import com.dnd.dndtravel.auth.repository.RefreshTokenRepository; import com.dnd.dndtravel.map.repository.MemberAttractionRepository; import com.dnd.dndtravel.map.repository.MemberRegionRepository; import com.dnd.dndtravel.member.domain.Member; @@ -15,6 +16,7 @@ public class MemberService { private final MemberRepository memberRepository; private final MemberAttractionRepository memberAttractionRepository; private final MemberRegionRepository memberRegionRepository; + private final RefreshTokenRepository refreshTokenRepository; @Transactional public Member saveMember(String name, String email, String selectedColor) { @@ -30,5 +32,6 @@ public void withdrawMember(long memberId) { memberRepository.delete(member); memberAttractionRepository.deleteById(memberId); memberRegionRepository.deleteById(memberId); + refreshTokenRepository.deleteById(memberId); } } From 0cc829bbceead1f58d94c59f909009617a837927 Mon Sep 17 00:00:00 2001 From: min-0 Date: Fri, 6 Sep 2024 03:31:13 +0900 Subject: [PATCH 5/5] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=EA=B0=92=20=EA=B8=B8=EC=9D=B4=20=EC=A0=9C=ED=95=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit authorization code max:300 --- .../dndtravel/auth/controller/request/AppleWithdrawRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java b/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java index 0ba39a7..68b252a 100644 --- a/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java +++ b/src/main/java/com/dnd/dndtravel/auth/controller/request/AppleWithdrawRequest.java @@ -8,7 +8,7 @@ */ public record AppleWithdrawRequest( @NotBlank(message = "인증 코드가 존재하지 않습니다.") - @Size(max = 12, message = "인증 코드가 12자를 초과하였습니다.") + @Size(max = 300, message = "인증 코드 길이를 초과하였습니다.") String authorizationCode ) { }