-
Notifications
You must be signed in to change notification settings - Fork 0
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
feat: Discord 기반 OAuth2 인증 구현 (#12) #36
Conversation
- JwtProviderImpl은 추후 새로운 이슈로 구현할 것 (파일 변경 수 너무 많음..!)
@GetMapping("/login") | ||
fun login( | ||
@RequestParam code: String, | ||
@RequestParam socialType: SocialType, | ||
): ResponseEntity<ApiResponse<LoginResponse>> { | ||
val response = oAuth2FacadeService.login(code, socialType) | ||
return ResponseEntity.ok() | ||
.body(ApiResponse.success(response)) | ||
} |
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.
로그인 요청에서 HTTP 메소드는 GET
보단 POST
가 맞지만, 쿼리 파라미터를 사용해서 하나의 핸들러 메서드로 OAuth2 인증을 처리하기 위해 GET
을 사용했습니다!
private fun handleAccessTokenError(clientResponse: ClientResponse): Exception { | ||
return when (clientResponse.statusCode()) { | ||
HttpStatus.UNAUTHORIZED -> { | ||
BadRequestException("잘못된 OAuth2 Authorization 코드입니다.") | ||
} | ||
|
||
else -> { | ||
log.warn { "getAccessToken() 호출에서 ${clientResponse.statusCode()} 예외가 발생했습니다." } | ||
InternalServerError("Discord OAuth2 서버에 문제가 발생했습니다.") | ||
} | ||
} | ||
} |
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.
Discord AccessToken을 요청하는 과정에서 401
상태코드가 반환되는 것은 잘못된 code를 입력할 때 발생하더군요.
그 외는 설정을 잘못 입력할 때이므로, 서버 설정이 잘못되었다고 판단하여 InternalServerError
를 던졌습니다. (500 상태코드)
override fun getUserInfo(accessToken: String): UserInfo { | ||
return webClient.get() | ||
.uri("/api/users/@me") | ||
.header(HttpHeaders.AUTHORIZATION, "Bearer $accessToken") | ||
.retrieve() | ||
.onStatus({ it.is4xxClientError || it.is5xxServerError }) { | ||
log.warn { "getUserInfo() 호출에서 ${it.statusCode()} 예외가 발생했습니다." } | ||
throw InternalServerError("Discord OAuth2 서버에 문제가 발생했습니다.") | ||
} | ||
.bodyToMono<DiscordUserInfoResponse>() | ||
.block()!! | ||
.toUserInfo() | ||
} |
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.
userInfo를 요청하는 것은 사용자가 아닌, 서버에서 모두 통제하므로 500 상태코드를 반환하게 했습니다.
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class) | ||
data class DiscordUserInfoResponse( | ||
val id: String, | ||
val username: String, | ||
val email: String?, | ||
val avatar: String?, | ||
) { | ||
|
||
fun toUserInfo(): UserInfo { | ||
return UserInfo( | ||
email = email, | ||
nickname = username, | ||
socialType = SocialType.DISCORD, | ||
socialId = id, | ||
profileImage = avatar?.let { "https://cdn.discordapp.com/avatars/${id}/${avatar}.png" } | ||
) | ||
} | ||
} |
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.
https://discord.com/developers/docs/resources/user
디스코드에서 제공하는 User Object의 리턴 타입입니다.
id
, username
은 not null 이지만, email
, avatar
는 nullable 하더군요.
따라서 똑같이 필드를 nullable 하게 해주었습니다.
|
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.
고생 많으셨습니다!!!!!
저 같은경우 retrofit2을 사용하긴 했습니다! retorfit2 도 한번 사용해보세요 ㅎㅎ
Spring Security OAuth client 도입은 어떠세요?
관련 이슈
PR 세부 내용
Discord 기반의 OAuth2 인증 기능을 구현했습니다.
자체 JWT 토큰을 반환하는 기능은 TODO로 남겨두었습니다. (한 번에 전부 구현하려면 변경 내역이 너무 많아져서 그렇습니다..!)
외부 API를 호출하는 로직은 Spring WebFlux를 사용했습니다.
이전 프로젝트에서는 RestTemplate를 사용했는데, RestTemplate의 지원이 이제 종료되어 WebFlux를 사용하라고 하더라구요.
WebFlux의 특징이 논블럭킹 비동기를 제공한다는 특징이 있는데, 제대로 사용하려면 학습 곡선이 조금 있어 그냥
block()
를 사용하여 동기로 처리했습니다. (애초에 비동기로 처리할 수 없는 것이, 사용자는 토큰을 바로 얻어야해서.. 😂)WebFlux로 비동기로 처리할 껀덕지는 웹 크롤링을 수행할 때인데, Jsoup로 HTML을 크롤링 하는것 또한 WebFlux를 사용해 볼 수 있을 것 같네요.
개발, 테스트 환경에서 실제 OAuth2를 사용할 필요는 없으니,
LocalOAuth2Client
를 사용하면 될 것 같습니다.자세한 내용은 코드에 리뷰로 남기겠습니다.