-
Notifications
You must be signed in to change notification settings - Fork 2
Feat/#462 닉네임 변경 API 구현 #470
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 6 commits
9ef006e
95bd4c7
ee8bc8a
3d14050
ded41a5
d52d13c
9bfb040
621a625
4685592
5a64990
2847c8d
05fdcf1
514c475
f0e54f9
04af872
3d46182
72ea86a
d9115bf
21358a7
8ca6f07
9c6fc11
21f4d60
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| package shook.shook.auth.application; | ||
|
|
||
| import io.jsonwebtoken.Claims; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
| import shook.shook.auth.application.dto.ReissueAccessTokenResponse; | ||
| import shook.shook.auth.exception.AuthorizationException; | ||
| import shook.shook.auth.repository.InMemoryTokenPairRepository; | ||
|
|
||
| @Transactional(readOnly = true) | ||
| @RequiredArgsConstructor | ||
| @Service | ||
| public class TokenService { | ||
|
|
||
| public static final String EMPTY_REFRESH_TOKEN = "none"; | ||
| public static final String TOKEN_PREFIX = "Bearer "; | ||
|
|
||
| private final TokenProvider tokenProvider; | ||
| private final InMemoryTokenPairRepository inMemoryTokenPairRepository; | ||
|
|
||
| public void validateRefreshToken(final String refreshToken) { | ||
| if (refreshToken.equals(EMPTY_REFRESH_TOKEN)) { | ||
| throw new AuthorizationException.RefreshTokenNotFoundException(); | ||
| } | ||
| } | ||
|
|
||
| public String extractAccessToken(final String authorization) { | ||
| return authorization.substring(TOKEN_PREFIX.length()); | ||
|
||
| } | ||
|
|
||
| public ReissueAccessTokenResponse reissueAccessTokenByRefreshToken(final String refreshToken, | ||
| final String accessToken) { | ||
| final Claims claims = tokenProvider.parseClaims(refreshToken); | ||
| final Long memberId = claims.get("memberId", Long.class); | ||
| final String nickname = claims.get("nickname", String.class); | ||
|
|
||
| inMemoryTokenPairRepository.validateTokenPair(refreshToken, accessToken); | ||
|
||
| final String reissuedAccessToken = tokenProvider.createAccessToken(memberId, nickname); | ||
| inMemoryTokenPairRepository.addOrUpdateTokenPair(refreshToken, reissuedAccessToken); | ||
|
|
||
| return new ReissueAccessTokenResponse(reissuedAccessToken); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,9 +7,8 @@ | |
| import org.springframework.web.bind.annotation.PostMapping; | ||
| import org.springframework.web.bind.annotation.RequestHeader; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
| import shook.shook.auth.application.AuthService; | ||
| import shook.shook.auth.application.TokenService; | ||
| import shook.shook.auth.application.dto.ReissueAccessTokenResponse; | ||
| import shook.shook.auth.exception.AuthorizationException; | ||
| import shook.shook.auth.ui.openapi.AccessTokenReissueApi; | ||
|
|
||
| @RequiredArgsConstructor | ||
|
|
@@ -18,21 +17,18 @@ public class AccessTokenReissueController implements AccessTokenReissueApi { | |
|
|
||
| private static final String EMPTY_REFRESH_TOKEN = "none"; | ||
| private static final String REFRESH_TOKEN_KEY = "refreshToken"; | ||
| private static final String TOKEN_PREFIX = "Bearer "; | ||
|
|
||
| private final AuthService authService; | ||
| private final TokenService tokenService; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
|
|
||
| @PostMapping("/reissue") | ||
| public ResponseEntity<ReissueAccessTokenResponse> reissueAccessToken( | ||
| @CookieValue(value = REFRESH_TOKEN_KEY, defaultValue = EMPTY_REFRESH_TOKEN) final String refreshToken, | ||
| @RequestHeader(HttpHeaders.AUTHORIZATION) final String authorization | ||
| ) { | ||
| if (refreshToken.equals(EMPTY_REFRESH_TOKEN)) { | ||
| throw new AuthorizationException.RefreshTokenNotFoundException(); | ||
| } | ||
| final String accessToken = authorization.split(TOKEN_PREFIX)[1]; | ||
| final ReissueAccessTokenResponse response = | ||
| authService.reissueAccessTokenByRefreshToken(refreshToken, accessToken); | ||
| tokenService.validateRefreshToken(refreshToken); | ||
| final String accessToken = tokenService.extractAccessToken(authorization); | ||
| final ReissueAccessTokenResponse response = tokenService.reissueAccessTokenByRefreshToken(refreshToken, | ||
| accessToken); | ||
|
||
|
|
||
| return ResponseEntity.ok(response); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,7 +7,6 @@ | |
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
| import shook.shook.auth.application.TokenProvider; | ||
| import shook.shook.auth.application.dto.TokenPair; | ||
| import shook.shook.auth.exception.AuthorizationException; | ||
| import shook.shook.auth.repository.InMemoryTokenPairRepository; | ||
| import shook.shook.auth.ui.argumentresolver.MemberInfo; | ||
|
|
@@ -62,7 +61,7 @@ public Member findByIdAndNicknameThrowIfNotExist(final Long id, final Nickname n | |
|
|
||
| @Transactional | ||
| public void deleteById(final Long id, final MemberInfo memberInfo) { | ||
| final Member member = getMemberIfValidRequest(id, memberInfo); | ||
| final Member member = getMemberIfValidRequest(id, memberInfo.getMemberId()); | ||
|
|
||
| final List<KillingPartLike> membersExistLikes = likeRepository.findAllByMemberAndIsDeleted(member, false); | ||
|
|
||
|
|
@@ -71,8 +70,7 @@ public void deleteById(final Long id, final MemberInfo memberInfo) { | |
| memberRepository.delete(member); | ||
| } | ||
|
|
||
| private Member getMemberIfValidRequest(final Long memberId, final MemberInfo memberInfo) { | ||
| final long requestMemberId = memberInfo.getMemberId(); | ||
| private Member getMemberIfValidRequest(final Long memberId, final Long requestMemberId) { | ||
| final Member requestMember = findById(requestMemberId); | ||
| final Member targetMember = findById(memberId); | ||
| validateMemberAuthentication(requestMember, targetMember); | ||
|
|
@@ -99,28 +97,20 @@ private Member findById(final Long id) { | |
| } | ||
|
|
||
| @Transactional | ||
| public TokenPair updateNickname(final Long memberId, final MemberInfo memberInfo, | ||
| final NicknameUpdateRequest request) { | ||
| final Member member = getMemberIfValidRequest(memberId, memberInfo); | ||
| public boolean updateNickname(final Long memberId, final Long requestMemberId, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 위의
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재 memberInfo 의 id 만 쓰이고 있는 상태라 id 만 넘겨주는 걸로 통일했습니다 😄
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| final NicknameUpdateRequest request) { | ||
| final Member member = getMemberIfValidRequest(memberId, requestMemberId); | ||
| final Nickname nickname = new Nickname(request.getNickname()); | ||
|
|
||
| if (member.hasSameNickname(nickname)) { | ||
| return null; | ||
| return false; | ||
| } | ||
|
|
||
| validateDuplicateNickname(nickname); | ||
| member.updateNickname(nickname.getValue()); | ||
| memberRepository.save(member); | ||
|
|
||
|
Comment on lines
+110
to
+105
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분 jpa 더티채킹을 통해서 값이 수정되어서 memberRespository.save()를 하지 않아도 될 것 같습니다!
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋은 지적입니다 👍🏻 |
||
| return reissueTokenPair(member.getId(), member.getNickname()); | ||
| } | ||
|
|
||
| private TokenPair reissueTokenPair(final Long memberId, final String nickname) { | ||
| final String reissuedAccessToken = tokenProvider.createAccessToken(memberId, nickname); | ||
| final String reissuedRefreshToken = tokenProvider.createRefreshToken(memberId, nickname); | ||
| inMemoryTokenPairRepository.addOrUpdateTokenPair(reissuedRefreshToken, reissuedAccessToken); | ||
|
|
||
| return new TokenPair(reissuedAccessToken, reissuedRefreshToken); | ||
| return true; | ||
| } | ||
|
|
||
| private void validateDuplicateNickname(final Nickname nickname) { | ||
|
|
||
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package shook.shook.member.application.dto; | ||
|
|
||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import lombok.AccessLevel; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Schema(description = "닉네임 변경 응답") | ||
| @AllArgsConstructor | ||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||
| @Getter | ||
| public class NicknameUpdateResponse { | ||
|
|
||
| @Schema(description = "액세스 토큰", example = "lfahrg;aoiehflksejflakwjeglk") | ||
| private String accessToken; | ||
|
|
||
| @Schema(description = "변경된 닉네임", example = "shook") | ||
| private String nickname; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,13 +6,12 @@ | |
| import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||
| import jakarta.servlet.http.HttpServletResponse; | ||
| import jakarta.validation.Valid; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.DeleteMapping; | ||
| import org.springframework.web.bind.annotation.PatchMapping; | ||
| import org.springframework.web.bind.annotation.PathVariable; | ||
| import org.springframework.web.bind.annotation.RequestBody; | ||
| import shook.shook.auth.application.dto.ReissueAccessTokenResponse; | ||
| import shook.shook.auth.ui.argumentresolver.Authenticated; | ||
| import shook.shook.auth.ui.argumentresolver.MemberInfo; | ||
| import shook.shook.member.application.dto.NicknameUpdateRequest; | ||
|
|
@@ -61,6 +60,10 @@ ResponseEntity<Void> deleteMember( | |
| ) | ||
| @Parameters( | ||
| value = { | ||
| @Parameter( | ||
| name = "memberInfo", | ||
| hidden = true | ||
| ), | ||
|
Comment on lines
+63
to
+66
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오홍 👍 👍 |
||
| @Parameter( | ||
| name = "member_id", | ||
| description = "닉네임을 변경할 회원 id", | ||
|
|
@@ -74,10 +77,9 @@ ResponseEntity<Void> deleteMember( | |
| } | ||
| ) | ||
| @PatchMapping("/nickname") | ||
| ResponseEntity<ReissueAccessTokenResponse> updateNickname( | ||
| @PathVariable(name = "member_id") final Long memberId, | ||
| ResponseEntity<Void> updateNickname( | ||
| @Authenticated final MemberInfo memberInfo, | ||
| @RequestBody final NicknameUpdateRequest request, | ||
| final HttpServletResponse response | ||
| @PathVariable(name = "member_id") final Long memberId, | ||
| @Valid @RequestBody final NicknameUpdateRequest request | ||
| ); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
현재 TokenService 내부에
TokenProvider와InMemoryTokenPairRepository를 가지고 구성이 되어 있는데 MemberService와 AuthService는 TokenService를 의존하는 것이 아닌TokenProvider와InMemoryTokenPairRepository의존하고 있는데 이 부분이 전에 말했던 순환참조 때문이었나요? 그렇지 않으면 TokenService를 의존하는 것이 좋을 것 같은데 어떻게 생각하시나요?