diff --git a/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/AuthenticationService.java b/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/AuthenticationService.java index c599f1b02..41136ac33 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/AuthenticationService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/authentication/application/AuthenticationService.java @@ -129,16 +129,19 @@ public boolean validateToken(final String accessToken) { @Transactional public void withdrawal( final Oauth2Type oauth2Type, - final String oauth2AccessToken, + final String accessToken, final String refreshToken ) throws InvalidWithdrawalException { final OAuth2UserInformationProvider provider = providerComposite.findProvider(oauth2Type); - final UserInformationDto userInformationDto = provider.findUserInformation(oauth2AccessToken); - final User user = userRepository.findByOauthIdAndDeletedIsFalse(userInformationDto.findUserId()) + final PrivateClaims privateClaims = tokenDecoder.decode(TokenType.ACCESS, accessToken) + .orElseThrow(() -> + new InvalidTokenException("유효한 토큰이 아닙니다.") + ); + final User user = userRepository.findByIdAndDeletedIsFalse(privateClaims.userId()) .orElseThrow(() -> new InvalidWithdrawalException("탈퇴에 대한 권한 없습니다.")); user.withdrawal(); - blackListTokenService.registerBlackListToken(oauth2AccessToken, refreshToken); - provider.unlinkUserBy(oauth2AccessToken, user.getOauthId()); + blackListTokenService.registerBlackListToken(accessToken, refreshToken); + provider.unlinkUserBy(user.getOauthId()); } } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/authentication/configuration/KakaoProvidersConfigurationProperties.java b/backend/ddang/src/main/java/com/ddang/ddang/authentication/configuration/KakaoProvidersConfigurationProperties.java index c83341654..8810db2d0 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/authentication/configuration/KakaoProvidersConfigurationProperties.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/authentication/configuration/KakaoProvidersConfigurationProperties.java @@ -3,5 +3,5 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties("oauth2.client.providers.kakao") -public record KakaoProvidersConfigurationProperties(String userInfoUri, String userUnlinkUri) { +public record KakaoProvidersConfigurationProperties(String adminKey, String userInfoUri, String userUnlinkUri) { } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/authentication/infrastructure/oauth2/OAuth2UserInformationProvider.java b/backend/ddang/src/main/java/com/ddang/ddang/authentication/infrastructure/oauth2/OAuth2UserInformationProvider.java index d1b3f05cb..278da9fac 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/authentication/infrastructure/oauth2/OAuth2UserInformationProvider.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/authentication/infrastructure/oauth2/OAuth2UserInformationProvider.java @@ -8,5 +8,5 @@ public interface OAuth2UserInformationProvider { UserInformationDto findUserInformation(final String accessToken); - UserInformationDto unlinkUserBy(final String accessToken, final String oauthId); + UserInformationDto unlinkUserBy(final String oauthId); } diff --git a/backend/ddang/src/main/java/com/ddang/ddang/authentication/infrastructure/oauth2/kakao/KakaoUserInformationProvider.java b/backend/ddang/src/main/java/com/ddang/ddang/authentication/infrastructure/oauth2/kakao/KakaoUserInformationProvider.java index 859a0db78..55f19afc2 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/authentication/infrastructure/oauth2/kakao/KakaoUserInformationProvider.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/authentication/infrastructure/oauth2/kakao/KakaoUserInformationProvider.java @@ -22,6 +22,7 @@ public class KakaoUserInformationProvider implements OAuth2UserInformationProvider { private static final String TOKEN_TYPE = "Bearer "; + private static final String KAKAO_ADMIN_TOKEN_TYPE = "KakaoAK "; private static final String REST_TEMPLATE_MESSAGE_SEPARATOR = ":"; private static final int MESSAGE_INDEX = 0; @@ -58,16 +59,16 @@ public UserInformationDto findUserInformation(final String accessToken) { } @Override - public UserInformationDto unlinkUserBy(final String accessToken, final String oauthId) { + public UserInformationDto unlinkUserBy(final String oauthId) { final HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set(HttpHeaders.AUTHORIZATION, TOKEN_TYPE + accessToken); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.set(HttpHeaders.AUTHORIZATION, KAKAO_ADMIN_TOKEN_TYPE + providersConfigurationProperties.adminKey()); - final MultiValueMap parameters = new LinkedMultiValueMap<>(); - parameters.add("target_id_type", "user_id"); - parameters.add("target_id", oauthId); + final MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("target_id_type", "user_id"); + body.add("target_id", oauthId); - final HttpEntity> request = new HttpEntity<>(parameters, headers); + final HttpEntity> request = new HttpEntity<>(body, headers); try { final ResponseEntity response = restTemplate.exchange( diff --git a/backend/ddang/src/main/java/com/ddang/ddang/bid/application/BidService.java b/backend/ddang/src/main/java/com/ddang/ddang/bid/application/BidService.java index 8a04a8b13..847cd3f5b 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/bid/application/BidService.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/bid/application/BidService.java @@ -111,7 +111,7 @@ private Bid saveBid(final CreateBidDto bidDto, final Auction auction, final User public List readAllByAuctionId(final Long auctionId) { if (auctionRepository.existsById(auctionId)) { - final List bids = bidRepository.findByAuctionId(auctionId); + final List bids = bidRepository.findByAuctionIdOrderByIdAsc(auctionId); return bids.stream() .map(ReadBidDto::from) diff --git a/backend/ddang/src/main/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepository.java b/backend/ddang/src/main/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepository.java index 2f927c27e..7d12de1de 100644 --- a/backend/ddang/src/main/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepository.java +++ b/backend/ddang/src/main/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepository.java @@ -9,7 +9,7 @@ public interface JpaBidRepository extends JpaRepository { - List findByAuctionId(final Long id); + List findByAuctionIdOrderByIdAsc(final Long id); @Query("select b from Bid b where b.auction.id = :auctionId order by b.id desc limit 1") Bid findLastBidByAuctionId(@Param("auctionId") final Long id); diff --git a/backend/ddang/src/main/resources/application-local.yml b/backend/ddang/src/main/resources/application-local.yml index adbb27f43..2db9e094b 100644 --- a/backend/ddang/src/main/resources/application-local.yml +++ b/backend/ddang/src/main/resources/application-local.yml @@ -49,6 +49,7 @@ oauth2: client: providers: kakao: + admin-key: adminkey user-info-uri: https://kapi.kakao.com/v2/user/me user-unlink-uri: https://kapi.kakao.com/v1/user/unlink diff --git a/backend/ddang/src/main/resources/static/docs/docs.html b/backend/ddang/src/main/resources/static/docs/docs.html index e9ba9e7a7..e623aa38b 100644 --- a/backend/ddang/src/main/resources/static/docs/docs.html +++ b/backend/ddang/src/main/resources/static/docs/docs.html @@ -877,7 +877,7 @@

탈퇴

요청

-
DELETE /oauth2/withdrawal/kakao HTTP/1.1
+
POST /oauth2/withdrawal/kakao HTTP/1.1
 Content-Type: application/json
 Authorization: Bearer accessToken
 
@@ -1021,60 +1021,17 @@ 

응답

사용자 정보 수정

요청

-
-
-
PATCH /users HTTP/1.1
-Content-Type: multipart/form-data; boundary=6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm
-Authorization: Bearer accessToken
-
+
+

Unresolved directive in docs.adoc - include::/Users/jamie/Documents/IntelliJ_Project/2023-3-ddang/backend/ddang/build/generated-snippets/user-controller-test/사용자_정보를_수정한다/http-request.adoc[]

응답

-
-
-
HTTP/1.1 200 OK
-Content-Type: application/json
-
-{
-  "name" : "사용자1",
-  "profileImage" : "http://localhost:8080/users/images/1",
-  "reliability" : 4.6
-}
+
+

Unresolved directive in docs.adoc - include::/Users/jamie/Documents/IntelliJ_Project/2023-3-ddang/backend/ddang/build/generated-snippets/user-controller-test/사용자_정보를_수정한다/http-response.adoc[] +Unresolved directive in docs.adoc - include::/Users/jamie/Documents/IntelliJ_Project/2023-3-ddang/backend/ddang/build/generated-snippets/user-controller-test/사용자_정보를_수정한다/response-fields.adoc[]

- ----- - - - - - - - - - - - - - - - - - - - - - - - - -
PathTypeDescription

name

String

사용자 닉네임

profileImage

String

사용자 프로필 이미지

reliability

Number

사용자 신뢰도

-
@@ -1442,7 +1399,7 @@

요청

Content-Disposition: form-data; name=request; filename=request Content-Type: application/json -{"title":"경매 상품 1","description":"이것은 경매 상품 1 입니다.","bidUnit":1000,"startPrice":1000,"closingTime":"2023-09-18T18:56:49.863763","subCategoryId":2,"thirdRegionIds":[3]} +{"title":"경매 상품 1","description":"이것은 경매 상품 1 입니다.","bidUnit":1000,"startPrice":1000,"closingTime":"2023-09-21T21:19:23.572151","subCategoryId":2,"thirdRegionIds":[3]} --6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm--
@@ -1498,7 +1455,7 @@

응답

{ "id" : 1, "title" : "title", - "image" : "http://localhost:8080/auctions/images1", + "image" : "http://localhost:8080/auctions/images/1", "auctionPrice" : 1000, "status" : "UNBIDDEN", "auctioneerCount" : 0 @@ -1616,14 +1573,14 @@

응답

"auctions" : [ { "id" : 2, "title" : "경매 상품 2", - "image" : "http://localhost:8080/auctions/images1", + "image" : "http://localhost:8080/auctions/images/1", "auctionPrice" : 1000, "status" : "FAILURE", "auctioneerCount" : 2 }, { "id" : 1, "title" : "경매 상품 1", - "image" : "http://localhost:8080/auctions/images1", + "image" : "http://localhost:8080/auctions/images/1", "auctionPrice" : 1000, "status" : "FAILURE", "auctioneerCount" : 2 @@ -2038,7 +1995,7 @@

응답

{ "auction" : { "id" : 1, - "images" : [ "http://localhost:8080/auctions/images1" ], + "images" : [ "http://localhost:8080/auctions/images/1" ], "title" : "경매 상품 1", "category" : { "main" : "main", @@ -2049,8 +2006,8 @@

응답

"lastBidPrice" : null, "status" : "FAILURE", "bidUnit" : 1000, - "registerTime" : "2023-09-15T18:56:49", - "closingTime" : "2023-09-15T18:56:49", + "registerTime" : "2023-09-18T21:19:23", + "closingTime" : "2023-09-18T21:19:23", "directRegions" : [ { "first" : "서울특별시", "second" : "강서구", @@ -2382,12 +2339,12 @@

응답

"name" : "사용자1", "profileImage" : "http://localhost:8080/users/images/1", "price" : 10000, - "bidTime" : "2023-09-15T18:56:51" + "bidTime" : "2023-09-18T21:19:25" }, { "name" : "사용자2", "profileImage" : "http://localhost:8080/users/images/2", "price" : 12000, - "bidTime" : "2023-09-15T18:56:51" + "bidTime" : "2023-09-18T21:19:25" } ] }
@@ -2578,11 +2535,11 @@

응답

"auction" : { "id" : 1, "title" : "경매1", - "image" : "http://localhost:8080/auctions/images1", + "image" : "http://localhost:8080/auctions/images/1", "price" : 10000 }, "lastMessage" : { - "createdAt" : "2023-09-15T18:56:53", + "createdAt" : "2023-09-18T21:19:27", "contents" : "메시지1" }, "isChatAvailable" : true @@ -2596,11 +2553,11 @@

응답

"auction" : { "id" : 2, "title" : "경매2", - "image" : "http://localhost:8080/auctions/images1", + "image" : "http://localhost:8080/auctions/images/1", "price" : 20000 }, "lastMessage" : { - "createdAt" : "2023-09-15T18:56:53", + "createdAt" : "2023-09-18T21:19:27", "contents" : "메시지2" }, "isChatAvailable" : true @@ -2766,7 +2723,7 @@

응답

"auction" : { "id" : 1, "title" : "경매 상품 1", - "image" : "http://localhost:8080/auctions/images1", + "image" : "http://localhost:8080/auctions/images/1", "price" : 3000 }, "isChatAvailable" : true @@ -3038,7 +2995,7 @@

응답

[ { "id" : 1, - "createdAt" : "2023-09-15T18:56:53", + "createdAt" : "2023-09-18T21:19:27", "isMyMessage" : true, "contents" : "메시지내용" } ] @@ -3188,7 +3145,7 @@

응답

"id" : 1, "name" : "회원1" }, - "createdTime" : "2023-09-15T18:56:55", + "createdTime" : "2023-09-18T21:19:29", "auction" : { "id" : 1, "title" : "제목" @@ -3200,7 +3157,7 @@

응답

"id" : 2, "name" : "회원2" }, - "createdTime" : "2023-09-15T18:56:55", + "createdTime" : "2023-09-18T21:19:29", "auction" : { "id" : 1, "title" : "제목" @@ -3212,7 +3169,7 @@

응답

"id" : 3, "name" : "회원3" }, - "createdTime" : "2023-09-15T18:56:55", + "createdTime" : "2023-09-18T21:19:29", "auction" : { "id" : 1, "title" : "제목" @@ -3376,7 +3333,7 @@

응답

"id" : 1, "name" : "회원1" }, - "createdTime" : "2023-09-15T18:56:55", + "createdTime" : "2023-09-18T21:19:29", "chatRoom" : { "id" : 1 }, @@ -3387,7 +3344,7 @@

응답

"id" : 1, "name" : "회원1" }, - "createdTime" : "2023-09-15T18:56:55", + "createdTime" : "2023-09-18T21:19:29", "chatRoom" : { "id" : 1 }, @@ -3398,7 +3355,7 @@

응답

"id" : 1, "name" : "회원1" }, - "createdTime" : "2023-09-15T18:56:55", + "createdTime" : "2023-09-18T21:19:29", "chatRoom" : { "id" : 1 }, @@ -3535,7 +3492,7 @@

응답

diff --git a/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/AuthenticationServiceTest.java b/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/AuthenticationServiceTest.java index 9691407ee..651a9cdce 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/AuthenticationServiceTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/authentication/application/AuthenticationServiceTest.java @@ -330,7 +330,12 @@ void setUp( userRepository.save(user); - final Map privateClaims = Map.of("userId", user.getId()); + final Map privateClaims = Map.of("userId", 1L); + final String accessToken = "Bearer " + tokenEncoder.encode( + LocalDateTime.now(), + TokenType.ACCESS, + privateClaims + ); final String refreshToken = "Bearer " + tokenEncoder.encode( LocalDateTime.now(), TokenType.REFRESH, @@ -341,11 +346,10 @@ void setUp( given(mockProviderComposite.findProvider(Oauth2Type.KAKAO)).willReturn(mockProvider); given(mockProvider.findUserInformation(anyString())).willReturn(userInformationDto); - given(mockProvider.unlinkUserBy(anyString(), anyString())).willReturn(userInformationDto); willDoNothing().given(mockBlackListTokenService).registerBlackListToken(anyString(), anyString()); // when - authenticationService.withdrawal(Oauth2Type.KAKAO, "accessToken", refreshToken); + authenticationService.withdrawal(Oauth2Type.KAKAO, accessToken, refreshToken); // then assertThat(user.isDeleted()).isTrue(); @@ -363,23 +367,28 @@ void setUp( userRepository.save(user); - final Map privateClaims = Map.of("userId", user.getId()); + user.withdrawal(); + + final Map privateClaims = Map.of("userId", 1L); + final String accessToken = "Bearer " + tokenEncoder.encode( + LocalDateTime.now(), + TokenType.ACCESS, + privateClaims + ); final String refreshToken = "Bearer " + tokenEncoder.encode( LocalDateTime.now(), TokenType.REFRESH, privateClaims ); - user.withdrawal(); - final UserInformationDto userInformationDto = new UserInformationDto(12345L); given(mockProviderComposite.findProvider(Oauth2Type.KAKAO)).willReturn(mockProvider); given(mockProvider.findUserInformation(anyString())).willReturn(userInformationDto); - given(mockProvider.unlinkUserBy(anyString(), anyString())).willReturn(userInformationDto); + given(mockProvider.unlinkUserBy(anyString())).willReturn(userInformationDto); // when && then - assertThatThrownBy(() -> authenticationService.withdrawal(Oauth2Type.KAKAO, "accessToken", refreshToken)) + assertThatThrownBy(() -> authenticationService.withdrawal(Oauth2Type.KAKAO, accessToken, refreshToken)) .isInstanceOf(InvalidWithdrawalException.class) .hasMessage("탈퇴에 대한 권한 없습니다."); } @@ -391,12 +400,12 @@ void setUp( given(mockProvider.findUserInformation(anyString())) .willThrow(new InvalidTokenException("401 Unauthorized")); - final String invalidAccessToken = "invalidAccessToken"; - final String invalidRefreshToken = "invalidRefreshToken"; + final String invalidAccessToken = "Bearer invalidAccessToken"; + final String invalidRefreshToken = "Bearer invalidRefreshToken"; // when & then assertThatThrownBy(() -> authenticationService.withdrawal(Oauth2Type.KAKAO, invalidAccessToken, invalidRefreshToken)) .isInstanceOf(InvalidTokenException.class) - .hasMessage("401 Unauthorized"); + .hasMessage("유효한 토큰이 아닙니다."); } } diff --git a/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/oauth2/kakao/KakaoUserInformationProviderTest.java b/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/oauth2/kakao/KakaoUserInformationProviderTest.java index 1e15e2108..29484d9a7 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/oauth2/kakao/KakaoUserInformationProviderTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/authentication/infrastructure/oauth2/kakao/KakaoUserInformationProviderTest.java @@ -105,11 +105,10 @@ void setUp() { ) ); - final String accessToken = "Bearer accessToken"; final String oauthId = "12345"; // when - final UserInformationDto actual = provider.unlinkUserBy(accessToken, oauthId); + final UserInformationDto actual = provider.unlinkUserBy(oauthId); // then assertThat(actual.id()).isEqualTo(userInformationDto.id()); diff --git a/backend/ddang/src/test/java/com/ddang/ddang/authentication/presentation/AuthenticationControllerTest.java b/backend/ddang/src/test/java/com/ddang/ddang/authentication/presentation/AuthenticationControllerTest.java index 5f300a161..f13d55333 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/authentication/presentation/AuthenticationControllerTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/authentication/presentation/AuthenticationControllerTest.java @@ -1,6 +1,7 @@ package com.ddang.ddang.authentication.presentation; import com.ddang.ddang.authentication.application.AuthenticationService; +import com.ddang.ddang.authentication.application.AuthenticationUserService; import com.ddang.ddang.authentication.application.BlackListTokenService; import com.ddang.ddang.authentication.application.dto.TokenDto; import com.ddang.ddang.authentication.application.exception.InvalidWithdrawalException; @@ -78,6 +79,9 @@ class AuthenticationControllerTest { @Autowired AuthenticationController authenticationController; + @MockBean + AuthenticationUserService authenticationUserService; + @Autowired RestDocumentationResultHandler restDocs; @@ -336,9 +340,9 @@ void setUp(@Autowired RestDocumentationContextProvider provider) { // when & then mockMvc.perform(RestDocumentationRequestBuilders.post("/oauth2/withdrawal/{oauth2Type}", "kakao") + .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(request)) - .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken") ) .andExpectAll( status().isForbidden(), diff --git a/backend/ddang/src/test/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepositoryTest.java b/backend/ddang/src/test/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepositoryTest.java index f7e51da31..94bfe4770 100644 --- a/backend/ddang/src/test/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepositoryTest.java +++ b/backend/ddang/src/test/java/com/ddang/ddang/bid/infrastructure/persistence/JpaBidRepositoryTest.java @@ -144,7 +144,7 @@ class JpaBidRepositoryTest { em.clear(); // when - final List actual = bidRepository.findByAuctionId(auction1.getId()); + final List actual = bidRepository.findByAuctionIdOrderByIdAsc(auction1.getId()); // then SoftAssertions.assertSoftly(softAssertions -> {