반응형
마지막으로 카카오 로그인이다.
저번에도 사용한
OauthService interface
interface OauthService {
fun getOauth(code: String, isRemote: Boolean): Oauth
fun getAccessToken(code: String, isRemote: Boolean): String
}
Oauth data class
@Entity
@Table(
indexes = [
Index(name = "idx_type_id", columnList = "oauthType, oauthId")
]
)
data class Oauth(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Int?,
@ManyToOne
var user: User?,
@Column(length = 100)
var oauthId: String,
@Enumerated(EnumType.ORDINAL)
var oauthType: OauthTypeEnum
)
그대로 사용한다.
우선 마찬가지로 카카오 애플리케이션 등록을 해야한다.
그 후에는 또 마찬가지로 redirect_uri를 지정해야 한다.
카카오 로그인을 ON으로 해주고
아래에 있는
본인이 사용할 redirect_uri를 지정해준다.
또한 앱 키 페이지에 있는 Rest API키를 가져와서 스프링부트의 환경변수에 넣어둔다.
- AccessToken 받아오기
바로 AccessToken을 받아오도록 해보자.
마찬가지로 사용자가 넘겨주는 code를 post로 요청해 토큰을 가져오는 것이다.
private final val kakaoAccessTokenServer = "https://kauth.kakao.com/oauth/token"
override fun getAccessToken(code: String, isRemote: Boolean): String {
val restTemplate = RestTemplate()
val httpHeaders = HttpHeaders()
httpHeaders.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
val httpBody = LinkedMultiValueMap<String, String>()
httpBody.add("redirect_uri", if (isRemote) redirectUri else localRedirectUri)
httpBody.add("client_id", clientId)
httpBody.add("code",code)
return restTemplate.postForObject(kakaoAccessTokenServer, this.getHttpEntity(httpBody), KakaoAccessToken::class.java)?.accessToken ?: ""
}
private data class KakaoAccessToken(
@JsonProperty("access_token")
val accessToken: String
)
fun getHttpEntity(httpBody: LinkedMultiValueMap<String, String>): HttpEntity<LinkedMultiValueMap<String, String>> {
val httpHeaders = HttpHeaders()
httpHeaders.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
httpBody.add("grant_type", "authorization_code")
return HttpEntity(httpBody, httpHeaders)
}
이렇게 해당 주소로 redirect_uri, client_id(Rest API키), code, grant_type을 넣어 post 요청하면 Body로 access_token이 하나 나오게 된다.
- 사용자 정보 가져오기
이제 사용자의 정보를 가져오자.
마찬가지로 응답받은 AccessToken을 그대로 넣어 get 요청하면 된다.
private final val kakaoUserInfoServer = "https://kapi.kakao.com/v2/user/me"
override fun getOauth(code: String, isRemote: Boolean): Oauth {
val accessToken = getAccessToken(code, isRemote)
val restTemplate = RestTemplate()
val kakaoUserInfo = restTemplate.exchange(
RequestEntity<Any>(this.getHttpHeadersWithAuthorization(accessToken), HttpMethod.POST, URI.create(kakaoUserInfoServer)),
KakaoUserInfo::class.java
).body
val kakaoUserId = kakaoUserInfo?.id ?: ""
return this.getOauthFromOauthIdAndOauthType(
oauthId = kakaoUserId,
oauthTypeEnum = OauthTypeEnum.KAKAO)
}
private data class KakaoUserInfo(
@JsonProperty("id") val id: String,
@JsonProperty("connected_at") val connectedAt: String,
@JsonProperty("properties") val properties: Properties,
@JsonProperty("kakao_account") val kakaoAccount: KakaoAccount
)
private data class Properties(
@JsonProperty("nickname") val nickname: String
)
private data class KakaoAccount(
@JsonProperty("profile_nickname_needs_agreement") val profileNicknameNeedsAgreement: Boolean,
@JsonProperty("profile") val profile: Profile
)
private data class Profile(
@JsonProperty("nickname") val nickname: String,
@JsonProperty("is_default_nickname") val isDefaultNickname: Boolean,
@JsonProperty("profile_image_url") val profileImageUrl:String?
)
fun getHttpHeadersWithAuthorization(accessToken: String): HttpHeaders{
val httpHeaders = HttpHeaders()
httpHeaders.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
httpHeaders.add("Authorization", "Bearer $accessToken")
return httpHeaders
}
저번처럼 요청을 하며, 응답받은 json 형식도 위와 같다.
이 중에서는 카카오의 동의항목에 따라 오지 않는 데이터가 있을 수도 있다.
그리고 여기서도 id를 통해 로그인한 사용자를 고유번호를 통하여 식별할 수 있다.
아래는 사용한 전체의 코드이다.
@Service
class KakaoOauthServiceImpl(
@Value("\${kakao.auth.client_id}")
private val clientId: String,
@Value("\${kakao.auth.redirect_uri}")
private val redirectUri: String,
@Value("\${kakao.auth.local_redirect_uri}")
private val localRedirectUri: String,
private val oauthRepository: OauthRepository
) : OauthService, OauthServiceImpl(oauthRepository) {
private final val kakaoAccessTokenServer = "https://kauth.kakao.com/oauth/token"
private final val kakaoUserInfoServer = "https://kapi.kakao.com/v2/user/me"
override fun getOauth(code: String, isRemote: Boolean): Oauth {
val accessToken = getAccessToken(code, isRemote)
val restTemplate = RestTemplate()
val kakaoUserInfo = restTemplate.exchange(
RequestEntity<Any>(this.getHttpHeadersWithAuthorization(accessToken), HttpMethod.POST, URI.create(kakaoUserInfoServer)),
KakaoUserInfo::class.java
).body
val kakaoUserId = kakaoUserInfo?.id ?: ""
return this.getOauthFromOauthIdAndOauthType(
oauthId = kakaoUserId,
oauthTypeEnum = OauthTypeEnum.KAKAO)
}
override fun getAccessToken(code: String, isRemote: Boolean): String {
val restTemplate = RestTemplate()
val httpHeaders = HttpHeaders()
httpHeaders.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
val httpBody = LinkedMultiValueMap<String, String>()
httpBody.add("redirect_uri", if (isRemote) redirectUri else localRedirectUri)
httpBody.add("client_id", clientId)
httpBody.add("code",code)
return restTemplate.postForObject(kakaoAccessTokenServer, this.getHttpEntity(httpBody), KakaoAccessToken::class.java)?.accessToken ?: ""
}
private data class KakaoAccessToken(
@JsonProperty("access_token")
val accessToken: String
)
private data class KakaoUserInfo(
@JsonProperty("id") val id: String,
@JsonProperty("connected_at") val connectedAt: String,
@JsonProperty("properties") val properties: Properties,
@JsonProperty("kakao_account") val kakaoAccount: KakaoAccount
)
private data class Properties(
@JsonProperty("nickname") val nickname: String
)
private data class KakaoAccount(
@JsonProperty("profile_nickname_needs_agreement") val profileNicknameNeedsAgreement: Boolean,
@JsonProperty("profile") val profile: Profile
)
private data class Profile(
@JsonProperty("nickname") val nickname: String,
@JsonProperty("is_default_nickname") val isDefaultNickname: Boolean,
@JsonProperty("profile_image_url") val profileImageUrl:String?
)
}
open class OauthServiceImpl(
private val oauthRepository: OauthRepository
) {
fun getHttpEntity(httpBody: LinkedMultiValueMap<String, String>): HttpEntity<LinkedMultiValueMap<String, String>> {
val httpHeaders = HttpHeaders()
httpHeaders.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
httpBody.add("grant_type", "authorization_code")
return HttpEntity(httpBody, httpHeaders)
}
fun getHttpHeadersWithAuthorization(accessToken: String): HttpHeaders{
val httpHeaders = HttpHeaders()
httpHeaders.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
httpHeaders.add("Authorization", "Bearer $accessToken")
return httpHeaders
}
fun getOauthFromOauthIdAndOauthType(oauthId: String, oauthTypeEnum: OauthTypeEnum): Oauth {
val optionalOauth = oauthRepository.findByOauthIdAndOauthType(oauthId, oauthTypeEnum)
if (optionalOauth.isPresent) {
CompletableFuture.supplyAsync{
optionalOauth.get()
}.thenApply {
oauthRepository.save(it)
}
return optionalOauth.get()
}
else{
return oauthRepository.save(Oauth(
id = null,
user = null,
oauthId = oauthId,
oauthType = oauthTypeEnum
))
}
}
}
이렇게 네이버, 구글, 카카오의 3가지 소셜로그인에 대하여 알아보았다.
다음에는 어떤 내용으로 글을 작성할지 고민해보도록 하겠다.
'크무톡톡 프로젝트' 카테고리의 다른 글
JDBC ON DUPLICATE KEY UPDATE (0) | 2024.07.29 |
---|---|
JUnit을 Kotlin으로 테스트 할 때 beforeAll, AfterAll (0) | 2024.07.26 |
구글 Oauth 로그인, SpringBoot (0) | 2024.07.24 |
네이버 Oauth 로그인, SpringBoot (2) | 2024.07.23 |
Nginx로 Swagger Proxy_pass (1) | 2024.07.22 |