-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat/LS-30: 회원가입, 로그인 응답 값 수정 & 회원 정보 얻기 API 구현 (#38)
* chore: 회원 가입 리턴값에 액세스 토큰 추가 * chore: 로그인 응답 값 수정 * feat: 회원 정보 얻기 API 작성 * docs: AuthApi 수정 * feat: 리프레시 토큰을 로그인, 회원가입 리턴값에 포함 * fix: 토큰 재발급 로직 수정
- Loading branch information
Showing
16 changed files
with
220 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,4 +60,4 @@ public OperationCustomizer customizeOperation() { | |
return operation; | ||
}; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
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 org.layer.common.annotation.MemberId; | ||
import org.layer.domain.auth.controller.dto.*; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
|
@@ -20,22 +21,25 @@ public interface AuthApi { | |
@ApiResponses({ | ||
@ApiResponse(responseCode = "200", description = "로그인 성공", | ||
headers = { | ||
@Header(name = "Authorization", description = "소셜 액세스 토큰(Bearer 없이 토큰만)", schema = @Schema(type = "string", format = "jwt"), required = true) | ||
@Header(name = "X-AUTH-TOKEN", description = "소셜 액세스 토큰(Bearer 없이 토큰만)", schema = @Schema(type = "string", format = "jwt"), required = true) | ||
}, | ||
content = @Content(mediaType = "application/json", examples = { | ||
@ExampleObject(name="로그인 성공", value = """ | ||
{ | ||
"memberId": 1, | ||
"accessToken": "eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE3MjA2OTcyMDksImV4cCI6MTcyMDY5OTAwOSwicm9sZSI6WyJVU0VSIl0sIm1lbWJlcklkIjoxfQ.OV-RWbIPZIQlMsPMR0reFHMFq9MNBKQwf7Hw7Uo0QbJPrTEACu0MqSJlv-gMtag1PhBxo7KB5dxEDza6QI06Zw", | ||
"refreshToken": "eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE3MjA2OTcyMTAsImV4cCI6MTcyMTkwNjgxMCwicm9sZSI6WyJVU0VSIl0sIm1lbWJlcklkIjoxfQ.fIVauBlL3GHLrVFJ1YwWb89RFwxa84Cql2WqEu4L258ebPJ04TkAGbqrCt7i-oEKI6dbvv0KDRKXkgDQH18kTA", | ||
"memberRole": "USER" | ||
"memberId": 3, | ||
"name": "김회고", | ||
"email": "[email protected]", | ||
"memberRole": "USER", | ||
"socialId": "123456789", | ||
"socialType": "KAKAO", | ||
"accessToken": "[토큰값]" | ||
} | ||
""" | ||
) | ||
})), | ||
@ApiResponse(responseCode = "400", description = "로그인 실패 - 토큰이 유효하지 않음", | ||
headers = { | ||
@Header(name = "Authorization", description = "소셜 액세스 토큰(Bearer 없이 토큰만)", schema = @Schema(type = "string", format = "jwt"), required = true) | ||
@Header(name = "X-AUTH-TOKEN", description = "소셜 액세스 토큰(Bearer 없이 토큰만)", schema = @Schema(type = "string", format = "jwt"), required = true) | ||
}, | ||
content = @Content(mediaType = "application/json", examples = { | ||
@ExampleObject(name="토큰이 유효하지 않음", value = """ | ||
|
@@ -48,7 +52,7 @@ public interface AuthApi { | |
})), | ||
@ApiResponse(responseCode = "404", description = "로그인 실패 - 회원이 DB에 없음", | ||
headers = { | ||
@Header(name = "Authorization", description = "소셜 액세스 토큰(Bearer 없이 토큰만)", schema = @Schema(type = "string", format = "jwt"), required = true) | ||
@Header(name = "X-AUTH_TOKEN", description = "소셜 액세스 토큰(Bearer 없이 토큰만)", schema = @Schema(type = "string", format = "jwt"), required = true) | ||
}, | ||
content = @Content(mediaType = "application/json", examples = { | ||
@ExampleObject(name="회원이 DB에 없음", value = """ | ||
|
@@ -66,24 +70,25 @@ public interface AuthApi { | |
@ApiResponses({ | ||
@ApiResponse(responseCode = "201", description = "회원가입 성공", | ||
headers = { | ||
@Header(name = "Authorization", description = "소셜 액세스 토큰(Bearer 없이 토큰만)", schema = @Schema(type = "string", format = "jwt"), required = true) | ||
@Header(name = "X-AUTH_TOKEN", description = "소셜 액세스 토큰(Bearer 없이 토큰만)", schema = @Schema(type = "string", format = "jwt"), required = true) | ||
}, | ||
content = @Content(mediaType = "application/json", examples = { | ||
@ExampleObject(name="회원 가입 성공. 유저의 정보를 리턴", value = """ | ||
{ | ||
"memberId": 1, | ||
"memberId": 3, | ||
"name": "김회고", | ||
"email": "layerkim@kakao.com", | ||
"email": "kimlayer@kakao.com", | ||
"memberRole": "USER", | ||
"SocialId": "1234567890", | ||
"socialType": "KAKAO" | ||
"socialId": "123456789", | ||
"socialType": "KAKAO", | ||
"accessToken": "[토큰값]" | ||
} | ||
""" | ||
) | ||
})), | ||
@ApiResponse(responseCode = "400", description = "회원가입 실패", | ||
headers = { | ||
@Header(name = "Authorization", description = "소셜 액세스 토큰", schema = @Schema(type = "string", format = "jwt"), required = true) | ||
@Header(name = "X-AUTH-TOKEN", description = "소셜 액세스 토큰", schema = @Schema(type = "string", format = "jwt"), required = true) | ||
}, | ||
content = @Content(mediaType = "application/json", examples = { | ||
@ExampleObject(name="이미 가입된 회원", value = """ | ||
|
@@ -106,31 +111,47 @@ public interface AuthApi { | |
@Operation(summary = "로그아웃", description = "member_id를 전달하면 DB에서 리프레시 토큰을 지웁니다.") | ||
public ResponseEntity<?> signOut(SignOutRequest signOutRequest); | ||
|
||
@Operation(summary = "[인증 불필요] 토큰 재발급", description = "member_id를 전달하면 데이터베이스에 리프레시 토큰이 남아있지 확인하고 남아있다면 jwt(access + refresh)를 새로 발급합니다.") | ||
@Operation(summary = "[인증 불필요] 토큰 재발급", description = "헤더에 refresh token과 바디에 member_id를 전달하면 데이터베이스에 리프레시 토큰이 남아있는지, 유효한지 확인하고 남아있다면 jwt(access + refresh)를 새로 발급합니다.") | ||
@ApiResponses({ | ||
@ApiResponse(responseCode = "201", description = "토큰 재발급 성공", | ||
content = @Content(mediaType = "application/json", examples = { | ||
@ExampleObject(name="토큰 재발급 성공(리프레시 토큰이 DB에 남아있음, 기한 2주)", value = """ | ||
{ | ||
"memberId": 1, | ||
"accessToken": "eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE3MjA2OTMyOTMsImV4cCI6MTcyMDY5NTA5Mywicm9sZSI6WyJVU0VSIl0sIm1lbWJlcklkIjoxfQ.nt4Tj1jTihS-6U7j2wkzv4VbgzTkhSPWnjBC_yXe_GiOKn3eoJ0i9NuA7Dzw6e4w_B-ab_PHzdrhfzyeVoPJOg", | ||
"refreshToken": "eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE3MjA2OTMyOTMsImV4cCI6MTcyMTkwMjg5Mywicm9sZSI6WyJVU0VSIl0sIm1lbWJlcklkIjoxfQ.MROa3B266VcnQqGHpvu2Lh3JiwexOM4BTYQt_3Tbc7xMY1AwS5Z51oAyVVZdO7wTLDLiUNe73DwR-7HNejWEdA" | ||
"memberId": 4, | ||
"name": "김세정", | ||
"email": "[email protected]", | ||
"memberRole": "USER", | ||
"socialId": "3612007072", | ||
"socialType": "KAKAO", | ||
"accessToken": "[토큰 값]", | ||
"refreshToken": "[토큰 값]" | ||
} | ||
""" | ||
) | ||
})), | ||
@ApiResponse(responseCode = "401", description = "토큰 재발급 실패", | ||
headers = { | ||
@Header(name = "Refresh", description = "자체 refresh token", schema = @Schema(type = "string", format = "jwt"), required = true) | ||
}, | ||
content = @Content(mediaType = "application/json", examples = { | ||
@ExampleObject(name="토큰 재발급 실패(리프레시 토큰이 DB에 없음)", value = """ | ||
{ | ||
"name": "NOT_FOUND_USER", | ||
"message": "유효한 유저를 찾지 못했습니다." | ||
} | ||
""" | ||
), | ||
@ExampleObject(name="토큰 재발급 실패(잘못된 리프레시 토큰)", value = """ | ||
{ | ||
"name": "INVALID_REFRESH_TOKEN", | ||
"message": "refresh token이 유효하지 않습니다." | ||
} | ||
""" | ||
) | ||
})) | ||
}) | ||
public ResponseEntity<ReissueTokenResponse> reissueToken(@RequestBody ReissueTokenRequest reissueTokenRequest); | ||
public ResponseEntity<ReissueTokenResponse> reissueToken(@RequestHeader(value = "Refresh", required = false) String refreshToken, | ||
@RequestBody ReissueTokenRequest reissueTokenRequest); | ||
|
||
|
||
@Operation(summary = "회원 탈퇴", description = "header Authorization에 액세스 토큰과 memberId를 전달하여 회원 탈퇴를 할 수 있습니다.") | ||
|
@@ -140,6 +161,21 @@ public interface AuthApi { | |
}) | ||
public ResponseEntity<?> withdraw(WithdrawMemberRequest withdrawMemberRequest); | ||
|
||
@Operation(summary = "회원 정보 얻기", description = "header Authorization에 Bearer [액세스토큰]을 넣어 인증하면 회원 정보를 얻을 수 있습니다.") | ||
@ApiResponse(responseCode = "200", description = "회원 정보 얻기 성공", | ||
content = @Content(mediaType = "application/json", examples = { | ||
@ExampleObject(name="회원 정보 얻기 성공", value = """ | ||
{ | ||
"memberId": 3, | ||
"name": "김회고", | ||
"email": "[email protected]", | ||
"memberRole": "USER", | ||
"socialId": "123456789", | ||
"socialType": "KAKAO" | ||
} | ||
""")})) | ||
public MemberInfoResponse getMemberInfo(@MemberId Long memberId); | ||
|
||
// TODO: 토큰 확인용 임시 API 추후 삭제 | ||
@Operation(summary = "[실제 사용 X] 구글 액세스 토큰 받기", description = "서버 쪽에서 토큰을 확인하기 위한 API입니다! (실제 사용 X, 추후 삭제 예정)") | ||
public String googleTest(@RequestParam("code") String code); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
layer-api/src/main/java/org/layer/domain/auth/controller/dto/MemberInfoResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package org.layer.domain.auth.controller.dto; | ||
|
||
import lombok.Builder; | ||
import org.layer.domain.member.entity.Member; | ||
import org.layer.domain.member.entity.MemberRole; | ||
import org.layer.domain.member.entity.SocialType; | ||
|
||
|
||
@Builder | ||
public record MemberInfoResponse(Long memberId, | ||
String name, | ||
String email, | ||
MemberRole memberRole, | ||
String socialId, | ||
SocialType socialType) { | ||
public static MemberInfoResponse of(Member member) { | ||
return new MemberInfoResponse(member.getId(), | ||
member.getName(), | ||
member.getEmail(), | ||
member.getMemberRole(), | ||
member.getSocialId(), | ||
member.getSocialType()); | ||
} | ||
} |
30 changes: 26 additions & 4 deletions
30
layer-api/src/main/java/org/layer/domain/auth/controller/dto/ReissueTokenResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,33 @@ | ||
package org.layer.domain.auth.controller.dto; | ||
|
||
import lombok.Builder; | ||
import org.layer.domain.auth.service.dto.ReissueTokenServiceResponse; | ||
import org.layer.domain.jwt.JwtToken; | ||
import org.layer.domain.member.entity.Member; | ||
import org.layer.domain.member.entity.MemberRole; | ||
import org.layer.domain.member.entity.SocialType; | ||
|
||
public record ReissueTokenResponse(Long memberId, String accessToken, String refreshToken){ | ||
@Builder | ||
public record ReissueTokenResponse(Long memberId, | ||
String name, | ||
String email, | ||
MemberRole memberRole, | ||
String socialId, | ||
SocialType socialType, | ||
String accessToken, | ||
String refreshToken){ | ||
public static ReissueTokenResponse of(ReissueTokenServiceResponse rtsr) { | ||
return new ReissueTokenResponse(rtsr.memberId(), | ||
rtsr.jwtToken().getAccessToken(), | ||
rtsr.jwtToken().getRefreshToken()); | ||
Member member = rtsr.member(); | ||
JwtToken jwtToken = rtsr.jwtToken(); | ||
return ReissueTokenResponse.builder() | ||
.memberId(member.getId()) | ||
.name(member.getName()) | ||
.email(member.getEmail()) | ||
.memberRole(member.getMemberRole()) | ||
.socialType(member.getSocialType()) | ||
.socialId(member.getSocialId()) | ||
.accessToken(jwtToken.getAccessToken()) | ||
.refreshToken(jwtToken.getRefreshToken()) | ||
.build(); | ||
} | ||
} |
33 changes: 24 additions & 9 deletions
33
layer-api/src/main/java/org/layer/domain/auth/controller/dto/SignInResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,30 @@ | ||
package org.layer.domain.auth.controller.dto; | ||
|
||
import org.layer.domain.auth.service.dto.SignInServiceResponse; | ||
import lombok.Builder; | ||
import org.layer.domain.jwt.JwtToken; | ||
import org.layer.domain.member.entity.Member; | ||
import org.layer.domain.member.entity.MemberRole; | ||
import org.layer.domain.member.entity.SocialType; | ||
|
||
|
||
public record SignInResponse(Long memberId, String accessToken, String refreshToken, MemberRole memberRole) { | ||
public static SignInResponse of(SignInServiceResponse signInServiceResponse) { | ||
return new SignInResponse(signInServiceResponse.memberId(), | ||
signInServiceResponse.accessToken(), | ||
signInServiceResponse.refreshToken(), | ||
signInServiceResponse.memberRole() | ||
); | ||
@Builder | ||
public record SignInResponse(Long memberId, | ||
String name, | ||
String email, | ||
MemberRole memberRole, | ||
String socialId, | ||
SocialType socialType, | ||
String accessToken, | ||
String refreshToken) { | ||
public static SignInResponse of(Member member, JwtToken jwtToken) { | ||
return SignInResponse.builder() | ||
.memberId(member.getId()) | ||
.name(member.getName()) | ||
.email(member.getEmail()) | ||
.memberRole(member.getMemberRole()) | ||
.socialType(member.getSocialType()) | ||
.socialId(member.getSocialId()) | ||
.accessToken(jwtToken.getAccessToken()) | ||
.refreshToken(jwtToken.getRefreshToken()) | ||
.build(); | ||
} | ||
} |
Oops, something went wrong.