-
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.
Merge pull request #209 from ITZipProject/feature/user-sns-login
✨ 새 기능 : SNS 로그인 기능
- Loading branch information
Showing
14 changed files
with
313 additions
and
4 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
37 changes: 37 additions & 0 deletions
37
src/main/java/darkoverload/itzip/feature/user/controller/OAuthController.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,37 @@ | ||
package darkoverload.itzip.feature.user.controller; | ||
|
||
import darkoverload.itzip.feature.user.controller.request.GithubUserRequest; | ||
import darkoverload.itzip.feature.user.controller.request.GoogleUserRequest; | ||
import darkoverload.itzip.feature.user.service.OAuthService; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import jakarta.validation.Valid; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
@Tag(name = "OAuth", description = "SNS 로그인 기능 API") | ||
@RestController | ||
@RequestMapping("/oauth") | ||
@RequiredArgsConstructor | ||
public class OAuthController { | ||
private final OAuthService oAuthService; | ||
|
||
@Operation( | ||
summary = "구글 로그인", | ||
description = "구글 엑세스 토큰 입력받아 로그인에 필요한 itzip 엑세스 토큰을 발급하거나 회원가입 합니다." | ||
) | ||
@PostMapping("/google") | ||
public ResponseEntity<?> google(@RequestBody @Valid GoogleUserRequest googleUserRequest) { | ||
return oAuthService.google(googleUserRequest); | ||
} | ||
|
||
@Operation( | ||
summary = "깃허브 로그인", | ||
description = "깃허브 엑세스 토큰 입력받아 로그인에 필요한 itzip 엑세스 토큰을 발급하거나 회원가입 합니다." | ||
) | ||
@PostMapping("/github") | ||
public ResponseEntity<?> github(@RequestBody @Valid GithubUserRequest githubUserRequest) { | ||
return oAuthService.github(githubUserRequest); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
src/main/java/darkoverload/itzip/feature/user/controller/request/GithubUserInfo.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,28 @@ | ||
package darkoverload.itzip.feature.user.controller.request; | ||
|
||
import darkoverload.itzip.feature.user.domain.User; | ||
import darkoverload.itzip.feature.user.entity.Authority; | ||
import lombok.Getter; | ||
import lombok.Setter; | ||
|
||
/** | ||
* 깃허브 api 유저 정보 dto | ||
*/ | ||
@Getter | ||
@Setter | ||
public class GithubUserInfo { | ||
private String login; | ||
private String id; | ||
private String avatar_url; | ||
private String email; | ||
|
||
public User toUserDomain() { | ||
return User.builder() | ||
.email(this.email) | ||
.password(this.id) | ||
.authority(Authority.USER) | ||
.snsType("github") | ||
.imageUrl(avatar_url) | ||
.build(); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
src/main/java/darkoverload/itzip/feature/user/controller/request/GithubUserRequest.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,16 @@ | ||
package darkoverload.itzip.feature.user.controller.request; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import jakarta.validation.constraints.NotBlank; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
public class GithubUserRequest { | ||
@NotBlank(message = "깃허브 엑세스 토큰값을 입력해주세요.") | ||
@Schema(description = "깃허브 엑세스 토큰값", example = "gho_kEwNV1237NGFEyZsls8S7or3HzZGkm1huWce") | ||
public String accessToken; | ||
} |
30 changes: 30 additions & 0 deletions
30
src/main/java/darkoverload/itzip/feature/user/controller/request/GoogleUserInfo.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,30 @@ | ||
package darkoverload.itzip.feature.user.controller.request; | ||
|
||
import darkoverload.itzip.feature.user.domain.User; | ||
import darkoverload.itzip.feature.user.entity.Authority; | ||
import lombok.Getter; | ||
import lombok.Setter; | ||
|
||
/** | ||
* 구글 api 유저 정보 dto | ||
*/ | ||
@Getter | ||
@Setter | ||
public class GoogleUserInfo { | ||
private String id; | ||
private String email; | ||
private String name; | ||
private String given_name; | ||
private String family_name; | ||
private String picture; | ||
|
||
public User toUserDomain() { | ||
return User.builder() | ||
.email(this.email) | ||
.password(this.id) | ||
.authority(Authority.USER) | ||
.snsType("google") | ||
.imageUrl(picture) | ||
.build(); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
src/main/java/darkoverload/itzip/feature/user/controller/request/GoogleUserRequest.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,16 @@ | ||
package darkoverload.itzip.feature.user.controller.request; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import jakarta.validation.constraints.NotBlank; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
public class GoogleUserRequest { | ||
@NotBlank(message = "구글 엑세스 토큰값을 입력해주세요.") | ||
@Schema(description = "구글 엑세스 토큰값", example = "ya29.a0ARW5m77UXvL8KtAR1OOB8g0ttMvCAvu123EeFhTwSyRpGvyXeiRka8NeLyzDxKzxAdv8wxwFWLnJ-CnwT_LvTUL3W1Tr4fPdgaxhGfLqGJgxAQyKrvIBb58V8L7jNh0ytUevDw5UaCw8-h4uXsHAszjETzelUZZWdoaCgYKAdwSARISFQHGX2Mi8ZZcBbzDMd0IVfG0rqG8eQ0170") | ||
public String accessToken; | ||
} |
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
11 changes: 11 additions & 0 deletions
11
src/main/java/darkoverload/itzip/feature/user/service/OAuthService.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,11 @@ | ||
package darkoverload.itzip.feature.user.service; | ||
|
||
import darkoverload.itzip.feature.user.controller.request.GithubUserRequest; | ||
import darkoverload.itzip.feature.user.controller.request.GoogleUserRequest; | ||
import org.springframework.http.ResponseEntity; | ||
|
||
public interface OAuthService { | ||
ResponseEntity<?> google(GoogleUserRequest googleUserRequest); | ||
|
||
ResponseEntity<?> github(GithubUserRequest githubUserRequest); | ||
} |
138 changes: 138 additions & 0 deletions
138
src/main/java/darkoverload/itzip/feature/user/service/OAuthServiceImpl.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,138 @@ | ||
package darkoverload.itzip.feature.user.service; | ||
|
||
import darkoverload.itzip.feature.techinfo.service.blog.BlogCommandService; | ||
import darkoverload.itzip.feature.user.controller.request.GithubUserInfo; | ||
import darkoverload.itzip.feature.user.controller.request.GithubUserRequest; | ||
import darkoverload.itzip.feature.user.controller.request.GoogleUserInfo; | ||
import darkoverload.itzip.feature.user.controller.request.GoogleUserRequest; | ||
import darkoverload.itzip.feature.user.domain.User; | ||
import darkoverload.itzip.feature.user.repository.UserRepository; | ||
import darkoverload.itzip.global.config.response.code.CommonExceptionCode; | ||
import darkoverload.itzip.global.config.response.exception.RestApiException; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.web.reactive.function.client.WebClient; | ||
|
||
import java.util.Optional; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class OAuthServiceImpl implements OAuthService { | ||
|
||
private final UserService userService; | ||
private final UserRepository userRepository; | ||
private final BlogCommandService blogCommandService; | ||
|
||
@Override | ||
public ResponseEntity<?> google(GoogleUserRequest googleUserRequest) { | ||
// 구글 유저 정보 조회 | ||
GoogleUserInfo googleUserInfo = fetchGoogleUserInfo(googleUserRequest); | ||
|
||
// 로그인/회원가입 처리 | ||
return handleSnsLogin( | ||
googleUserInfo.toUserDomain() | ||
); | ||
} | ||
|
||
@Override | ||
public ResponseEntity<?> github(GithubUserRequest githubUserRequest) { | ||
// 깃허브 유저 정보 조회 | ||
GithubUserInfo githubUserInfo = fetchGithubUserInfo(githubUserRequest); | ||
|
||
// 로그인/회원가입 처리 | ||
return handleSnsLogin( | ||
githubUserInfo.toUserDomain() | ||
); | ||
} | ||
|
||
/** | ||
* SNS 로그인/회원가입 공통 처리 로직 | ||
* | ||
* @param userDomain user 도메인 | ||
* @return ResponseEntity | ||
*/ | ||
private ResponseEntity<?> handleSnsLogin(User userDomain) { | ||
Optional<User> userOptional = userService.findByEmail(userDomain.getEmail()); | ||
|
||
// 회원가입 | ||
if (!userOptional.isPresent()) { | ||
return save(userDomain); | ||
} | ||
|
||
User user = userOptional.get(); | ||
|
||
if (user.getSnsType() == null) { // 기존 이메일 회원이 SNS로 로그인 시도할 경우 예외 | ||
throw new RestApiException(CommonExceptionCode.EMAIL_USER_SNS_LOGIN); | ||
} | ||
|
||
if (!user.getSnsType().equals(userDomain.getSnsType())) { | ||
switch (user.getSnsType()) { | ||
case "google": | ||
throw new RestApiException(CommonExceptionCode.GOOGLE_USER_GITHUB_LOGIN); | ||
case "github": | ||
throw new RestApiException(CommonExceptionCode.GITHUB_USER_GOOGLE_LOGIN); | ||
} | ||
} | ||
|
||
// SNS 회원이라면 로그인 처리 | ||
return userService.loginResponse(user); | ||
} | ||
|
||
/** | ||
* 구글 유저 정보 가져오기 | ||
*/ | ||
private GoogleUserInfo fetchGoogleUserInfo(GoogleUserRequest googleUserRequest) { | ||
try { | ||
WebClient client = WebClient.create("https://www.googleapis.com"); | ||
return client | ||
.get() | ||
.uri(uriBuilder -> uriBuilder | ||
.path("/oauth2/v1/userinfo") | ||
.queryParam("access_token", googleUserRequest.getAccessToken()) | ||
.build() | ||
) | ||
.accept(MediaType.APPLICATION_JSON) | ||
.retrieve() | ||
.bodyToMono(GoogleUserInfo.class) | ||
.block(); | ||
} catch (Exception e) { | ||
// 구글 API 호출 오류 | ||
throw new RestApiException(CommonExceptionCode.FILED_ERROR); | ||
} | ||
} | ||
|
||
/** | ||
* 깃허브 유저 정보 가져오기 | ||
*/ | ||
private GithubUserInfo fetchGithubUserInfo(GithubUserRequest githubUserRequest) { | ||
try { | ||
WebClient client = WebClient.create("https://api.github.com"); | ||
return client | ||
.get() | ||
.uri("/user") | ||
.header(HttpHeaders.AUTHORIZATION, "token " + githubUserRequest.getAccessToken()) | ||
.accept(MediaType.APPLICATION_JSON) | ||
.retrieve() | ||
.bodyToMono(GithubUserInfo.class) | ||
.block(); | ||
} catch (Exception e) { | ||
// 깃허브 API 호출 오류 | ||
throw new RestApiException(CommonExceptionCode.FILED_ERROR); | ||
} | ||
} | ||
|
||
/** | ||
* SNS 로그인 회원가입 | ||
* | ||
* @param user 회원가입할 유저 | ||
*/ | ||
private ResponseEntity<String> save(User user) { | ||
user.setNickname(userService.getUniqueNickname()); // 닉네임 중복 방지 로직 | ||
User savedUser = userRepository.save(user.convertToEntity()).convertToDomain(); | ||
blogCommandService.create(savedUser); // 블로그 생성 로직 | ||
return ResponseEntity.ok("회원가입이 완료되었습니다."); | ||
} | ||
} |
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
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
Oops, something went wrong.