Kakao登录
kakao 介绍
Kakao 为韩国最大通讯公司
Kakao Login Android集成
Android 集成文档:
https://developers.kakao.com/docs/latest/en/kakaologin/android
MyApplication 设置
App Key 获取
https://developers.kakao.com/console/app[My Application] > [App Keys] or [My Application] > [Summary]
APP 关注 Native app key
Platform 设置
以 Android 为例:
- Packagename :应用的包名,即 applicationId,必选
- Market URL:应用市场链接,填上包名自动填写
- key hash:应用签名,需要用命令生成:
keytool -exportcert -alias <RELEASE_KEY_ALIAS> -keystore <RELEASE_KEY_PATH> | openssl sha1 -binary | openssl base64
Kakao Login
Kakao Login Activation
需要开启 Kakao Login Activation,否则获取不到 AccessToken
OpenID Connect Activation
Consent Items
初始化
1
2
3
4
5
6
7
8
9
class GlobalApplication : Application() {
override fun onCreate() {
super.onCreate()
// Other codes for initialization
// Initialize Android SDK
KakaoSdk.init(this, "${YOUR_NATIVE_APP_KEY}")
}
}
Before you begin login
Before using Kakao Login APIs with the Android SDK,
Register the Android platform
KaKao 注册平台登记后才能使用:[My Application] > [Platform] > Android/iOS/Web
- Package Name 应用的 applicationId
- Market URL 自动会生成
- Key hash
keytool -exportcert -alias
-keystore | openssl sha1 -binary | openssl base64 类似:8tG0AIdXBAyX5aN8w83ECrKYMxxx
add modules
Gradle Install
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// app build.gradle
// Allprojects not available in the latest Android Studio
allprojects {
repositories {
google()
jcenter()
maven { url 'https://devrepo.kakao.com/nexus/content/groups/public/'}
}
}
// settings.gradle
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url 'https://devrepo.kakao.com/nexus/content/groups/public/' }
}
}
// modules to the build.gradle(module-level) file.
dependencies {
implementation "com.kakao.sdk:v2-all:2.18.0" // Add all modules (Available in 2.11.0 or higher)
implementation "com.kakao.sdk:v2-user:2.18.0" // Kakao Login
implementation "com.kakao.sdk:v2-talk:2.18.0" // Kakao Talk Social, Kakao Talk Messaging
implementation "com.kakao.sdk:v2-friend:2.18.0" // Friend picker
implementation "com.kakao.sdk:v2-share:2.18.0" // Kakao Talk Sharing
implementation "com.kakao.sdk:v2-navi:2.18.0" // Kakao Navi
implementation "com.kakao.sdk:v2-cert:2.18.0" // Kakao Certificate
}
ProGuard rules
1
2
3
4
5
6
7
8
# kakao https://developers.kakao.com/docs/latest/en/android/getting-started#project-pro-guard
-keep class com.kakao.sdk.**.model.* { <fields>; }
-keep class * extends com.google.gson.TypeAdapter
# https://github.com/square/okhttp/pull/6792
-dontwarn org.bouncycastle.jsse.**
-dontwarn org.conscrypt.*
-dontwarn org.openjsse.**
Set Redirect URI
refscheme 填 kakao${YOUR_NATIVE_APP_KEY}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<activity
android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Redirect URI: "kakao${YOUR_NATIVE_APP_KEY}://oauth" -->
<data
android:host="oauth"
android:scheme="kakaob70bd3c3f3cdab01ccfe4cdd7f80542b" />
</intent-filter>
</activity>
Login
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
object KaKaoLoginHelper {
private const val TAG = "KaKaoLogin"
// 测试的app key
private const val APP_KEY = "b70bd3c3f3cdab01ccfe4cdd7f80542b"
fun init(context: Context) {
KakaoSdk.init(context, APP_KEY)
}
/**
* Check if KakaoTalk is installed on user's device
*
* @return true if KakaoTalk is installed on user's device, false otherwise
*/
fun isKakaoTalkLoginAvailable(context: Context): Boolean {
return UserApiClient.instance.isKakaoTalkLoginAvailable(context = context)
}
/**
* Login with Kakao Account/KakaoTalk
*/
fun login(context: Context, callback: (User?, Throwable?) -> Unit) {
// Login common callback
val internalCallback: (OAuthToken?, Throwable?) -> Unit = { token, error ->
if (error != null) {
// 登录过程中发生错误
loge("login 登录过程中发生错误:${error.message}", error)
error.printStackTrace()
callback.invoke(null, error)
} else if (token != null) {
// 登录成功,token 包含了访问令牌
Log.i(TAG, "login 登录成功,开始检索用户信息,token 包含了访问令牌:$token")
// 检索用户信息
getUserInfo { user: User?, throwable: Throwable? ->
if (throwable != null) {
// loge("login 登录成功,获取用户信息失败:${throwable.message}", throwable)
throwable.printStackTrace()
callback.invoke(null, throwable)
} else if (user != null) {
// logi("login 登录成功,检索用户信息成功,user(${user.id}):$user")
callback.invoke(user, null)
}
}
}
}
// If Kakao Talk is installed on user's device, proceed to log in with Kakao Talk. Otherwise, implement to log in with Kakao Account.
if (isKakaoTalkLoginAvailable(context)) {
logd("login with KakaoTalk")
UserApiClient.instance.loginWithKakaoTalk(context) { token, error ->
if (error != null) {
// After installing Kakao Talk, if a user does not complete app permission and cancels Login with Kakao Talk, skip to log in with Kakao Account, considering that the user does not want to log in.
// You could implement other actions such as going back to the previous page.
if (error is ClientError && error.reason == ClientErrorCause.Cancelled) {
loge("login with KakaoTalk failed cancelled: ${error.message}", error)
return@loginWithKakaoTalk
}
loge(
"login with KakaoTalk failed, attempt loginWithKakaoAccount, error: ${error.message}",
error
)
// If a user is not logged into Kakao Talk after installing Kakao Talk and allowing app permission, make the user log in with Kakao Account.
UserApiClient.instance.loginWithKakaoAccount(
context,
callback = internalCallback,
)
} else if (token != null) {
logd("login with KakaoTalk succeeded, accessToken=${token.accessToken}")
internalCallback.invoke(token, null)
}
}
} else {
logd("login with KakaoAccount")
UserApiClient.instance.loginWithKakaoAccount(context, callback = internalCallback)
}
}
fun loginWithKakaoTalk(context: Context, callback: (User?, Throwable?) -> Unit) {
// Login with Kakao Talk
UserApiClient.instance.loginWithKakaoTalk(context) { token, error ->
if (error != null) {
loge("loginWithKakaoTalk failed.", error)
callback.invoke(null, error)
} else if (token != null) {
logi("loginWithKakaoTalk succeeded.${token.accessToken}")
// 检索用户信息
getUserInfo { user: User?, throwable: Throwable? ->
if (throwable != null) {
// 获取用户信息失败
// loge("loginWithKakaoTalk 登录成功,获取用户信息失败:${throwable.message}", throwable)
throwable.printStackTrace()
callback.invoke(null, throwable)
} else if (user != null) {
// logi("loginWithKakaoTalk 登录成功,检索用户信息成功,user(${user.id}):$user")
callback.invoke(user, null)
}
}
}
}
}
fun loginWithKakao(context: Context, callback: (User?, Throwable?) -> Unit) {
UserApiClient.instance.loginWithKakaoAccount(context) { token, error ->
if (error != null) {
// 登录过程中发生错误
loge("loginWithKakao 登录过程中发生错误:${error.message}", error)
error.printStackTrace()
callback.invoke(null, error)
} else if (token != null) {
// 登录成功,token 包含了访问令牌
// logi("loginWithKakao 登录成功,开始检索用户信息,token 包含了访问令牌:$token")
// 检索用户信息
getUserInfo { user: User?, throwable: Throwable? ->
if (throwable != null) {
// loge("loginWithKakao 登录成功,获取用户信息失败:${throwable.message}", throwable)
throwable.printStackTrace()
callback.invoke(null, throwable)
} else if (user != null) {
// logi("loginWithKakao 登录成功,检索用户信息成功,user(${user.id}):$user")
callback.invoke(user, null)
}
}
}
}
}
/**
* Retrieve user information
*
* https://developers.kakao.com/docs/latest/en/kakaologin/android#req-user-info
*/
fun getUserInfo(callback: (User?, Throwable?) -> Unit = { _, _ -> }) {
UserApiClient.instance.me { user: User?, throwable: Throwable? ->
if (throwable != null) {
// 获取用户信息失败
loge("getUserInfo 登录成功,获取用户信息失败:${throwable.message}", throwable)
throwable.printStackTrace()
callback.invoke(null, throwable)
} else if (user != null) {
// 获取用户信息成功,这里可以处理用户信息
logi("getUserInfo 登录成功,检索用户信息成功,user(${user.id}):$user")
callback.invoke(user, null)
}
}
}
/**
* Retrieve token information
*
* https://developers.kakao.com/docs/latest/en/kakaologin/android#get-token-info
*/
fun accessTokenInfo(callback: (tokenInfo: AccessTokenInfo?, error: Throwable?) -> Unit) {
// However, note that the return value true does not guarantee that the user is in a logged-in state.
if (AuthApiClient.instance.hasToken()) {
UserApiClient.instance.accessTokenInfo { tokenInfo, error ->
if (error != null) {
loge("accessTokenInfo Failed to retrieve token information.", error)
} else if (tokenInfo != null) {
logi(
"accessTokenInfo Retrieving token information success" +
"\n App ID: ${tokenInfo.appId}" +
"\nService user ID: ${tokenInfo.id}" +
"\nValidity period: ${tokenInfo.expiresIn} seconds"
)
}
callback.invoke(tokenInfo, error)
}
} else {
loge("accessTokenInfo Failed to retrieve token information. No token.")
callback.invoke(null, null)
}
}
/**
* Logout, Regardless of the result of the logout request, the Android SDK deletes the access and refresh tokens and has the login session end.
*
* deletes the access and refresh tokens issued to the user.
*
* https://developers.kakao.com/docs/latest/en/kakaologin/android#logout
*/
fun logout(callback: (Throwable?) -> Unit) {
UserApiClient.instance.logout { error ->
if (error != null) {
loge("Logout failed: ${error.message}", error)
callback.invoke(error)
} else {
logi("Logout succeeded")
callback.invoke(null)
}
}
}
/**
* Unlink
*
* If the request is successful, the Android SDK deletes the access and refresh tokens. As the issued tokens are deleted, the session between an app and a user is disconnected, and the user is logged out and unlinked from your app.
*
* https://developers.kakao.com/docs/latest/en/kakaologin/android#unlink
*/
fun unlink(callback: (error: Throwable?) -> Unit) {
UserApiClient.instance.unlink { error ->
callback.invoke(error)
if (error != null) {
loge("Unlink fail", error)
} else {
logi("Unlink success. Tokens are deleted from SDK.")
}
}
}
fun loge(msg: String, e: Throwable? = null) {
Log.e(TAG, msg, e)
}
fun logi(msg: String) {
Log.i(TAG, msg)
}
fun logd(msg: String) {
Log.d(TAG, msg)
}
}
- 拿到 access_token 会拿到 userid
- 拿 userid 给中间层登录
遇到的问题
管理员设置问题导致授权不了
Error due to incorrect platform information:invalid android_key_hash or ios_bundle_id or web_site_url
解决:错误:invalid android_key_hash or ios_bundle_id or web_site_url
应用设置后台看看有没有注册对应的登录平台
Kakao SDK 评估
背景
亚洲站点、韩国市场,添加三方登录 Kakao
版本分析
新接入的 sdk,没有历史接入版本
影响范围
新功能,功能异常情况下会影响到 Kakao 用户的三方登录,需要做 abt 降级
价值评估
提升亚洲站点、韩国市场用户登录转化率
影响评估
App 质量
- github 没有相关仓库
- 最新版本的 SDK,App 质量暂时没办法验证,接入到测试的 Monkey 流程来测试稳定性,上线需观察, 发现有不可控的 Crash 问题需要 abt 降级
性能
待测试
包大小
aar 大小 (com.kakao.sdk:v2-user:2.18.0):342K
- user 103K
- network 22K
- common 90K
- auth 127K
兼容性
- Kakao 的 sdk 最低版本:Android6.0 及以上;而 xxx/yyy app 最低兼容版本是 Android5.0
- 其他兼容问题待测试
业务功能影响
新功能,功能异常情况下会影响到 Kakao 用户的三方登录,需要做 abt 降级
技术方案评估
UI、UE
无影响
接口
无影响
效果监控
不实施
风险评估
电子应用市场审核规则风险评估
待评估
安全评估
- 获取用户的 email, phone,隐私协议是否需要更新?
降级方案
隐藏 Kakao 登录方式