简介

HMS Core华为帐号注册用户量已达到10亿+,通过华为帐号可以一键登录应用,通过与华为帐号绑定,可以为应用快速引入新用户和登录。华为帐号开放遵循OAuth2.0以及OpenID Connect标准规范。
您需要:

您将建立什么

在这个Codelab中,您将使用已经创建好的Demo Project实现对华为帐号开放服务的API调用,通过Demo Project您可以体验到:

您将会学到什么

硬件要求

软件要求

集成HUAWEI HMS Core能力,需要完成以下准备工作:

具体操作,请按照《HUAWEI HMS Core集成准备》中详细说明来完成。

  1. 华为开发者联盟AppGallery Connect中选择"我的项目",在项目列表中选择创建的应用,在"项目设置"页面中选择"API管理"。
  2. 打开"Account Kit"服务开关。

至此,已经为创建的应用开通了"帐号"服务。

获取配置文件

  1. 打开华为开发者联盟AppGallery Connect应用管理中之前创建的应用,并选择"开发->常规",点击数据存储位置设置按钮。
  2. 根据应用实际情况,设置对应数据存储位置,然后点击确认按钮。
  3. 下载应用中的"agconnect-services.json"。
  4. 将 "agconnect-services.json"文件移至Android Studio开发工程应用模块的根目录。

配置HMS Core SDK依赖

  1. 打开应用级的build.gradle文件。
  2. 在"dependencies"中添加如下编译依赖,其中{version}替换为HMS Core SDK的版本号,例如:5.2.0.300
    dependencies { implementation 'com.huawei.hms:hwid:{version}' }
  3. 点击Sync Now 同步工程。

配置混淆脚本

  1. 打开Android Studio工程的混淆配置文件proguard-rules.pro
  2. 加入混淆配置。
    -ignorewarnings -keepattributes *Annotation* -keepattributes Exceptions -keepattributes InnerClasses -keepattributes Signature -keepattributes SourceFile,LineNumberTable -keep class com.huawei.hianalytics.**{*;} -keep class com.huawei.updatesdk.**{*;} -keep class com.huawei.hms.**{*;}
  3. 如果您使用了AndResGuard,需要在混淆配置文件中加入AndResGuard允许清单。
    "R.string.hms*", "R.string.connect_server_fail_prompt_toast", "R.string.getting_message_fail_prompt_toast", "R.string.no_available_network_prompt_toast", "R.string.third_app_*", "R.string.upsdk_*", "R.layout.hms*", "R.layout.upsdk_*", "R.drawable.upsdk*", "R.color.upsdk*", "R.dimen.upsdk*", "R.style.upsdk*", "R.string.agc*"

帐号的交互接口主要涉及登录、静默登录、退出帐号、取消授权。本次Codelab中您可以在您的Android Studio工程中创建一个布局页面,参照下图进行UI设计。

ID Token模式登录帐号

ID Token模式整体流程:

  1. 用户点击帐号授权登录图标,请求使用帐号授权登录。
  2. 帐号拉起用户登录授权界面,界面上会根据登录请求中携带的授权域(scopes)信息,显示告知用户需要授权的内容。
  3. 用户手动允许授权后,帐号返回ID Token信息。
  4. 应用选择使用本地方式验证或帐号服务器方式验证ID Token。

ID Token模式代码示例:

1. 展示华为登录方式图标。

应用在登录页面展示帐号登录方式的图标,华为登录方式图标规范请参见华为帐号登录图标使用规范

2. 调用AccountAuthParamsHelper的setIdToken方法请求授权。

Java

AccountAuthParams authParams = new AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM).setIdToken().createParams();

Kotlin

val authParams : AccountAuthParams = AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM).setIdToken().createParams()

3. 调用AccountAuthManager的 getService方法初始化AccountAuthService对象。

Java

AccountAuthService service = AccountAuthManager.getService(MainActivity.this, authParams);

Kotlin

val service : AccountAuthService = AccountAuthManager.getService(this@MainActivity, authParams)

4. 调用AccountAuthService的getSignInIntent方法并拉起帐号登录授权页面。

Java

startActivityForResult(service.getSignInIntent(), 8888);

Kotlin

startActivityForResult(service.signInIntent, 8888)

5. 登录授权完成后处理登录结果。

Java

@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { //授权登录结果处理,从AuthAccount中获取ID Token super.onActivityResult(requestCode, resultCode, data); if (requestCode == 8888) { Task<AuthAccount> authAccountTask = AccountAuthManager.parseAuthResultFromIntent(data); if (authAccountTask.isSuccessful()) { //登录成功,获取用户的帐号信息和ID Token AuthAccount authAccount = authAccountTask.getResult(); Log.i(TAG, "idToken:" + authAccount.getIdToken()); } else { //登录失败,不需要做处理,打点日志方便定位 Log.e(TAG, "sign in failed : " + ((ApiException) authAccountTask.getException()).getStatusCode()); } } }

Kotlin

override fun onActivityResult(requestCode:Int, resultCode:Int, data:Intent?) { //授权登录结果处理,从AuthAccount中获取ID Token super.onActivityResult(requestCode, resultCode, data) if (requestCode == 8888) { val authAccountTask = AccountAuthManager.parseAuthResultFromIntent(data) if (authAccountTask.isSuccessful) { //登录成功,获取用户的帐号信息和ID Token val authAccount = authAccountTask.result Log.i(TAG, "idToken:" + authAccount.idToken) } else { //登录失败,不需要做处理,打点日志方便定位 Log.e(TAG, "sign in failed : " + (authAccountTask.exception as ApiException).statusCode) } } }

Authorization Code模式登录帐号

帐号还支持使用Authorization Code授权登录。适用于拥有server端的应用授权。

Authorization Code整体流程:

  1. 用户点击帐号授权登录图标,请求使用帐号授权登录。
  2. 帐号拉起用户授权界面,界面上会根据登录请求中携带的授权域(scopes)信息, 显示告知用户需要授权的内容。
  3. 用户手动允许授权后,帐号返回Authorization Code信息。
  4. 第三方应用获取Authorization Code后,通过服务端接口获取Access Token、Refresh Token、Id Token信息。
  5. Access Token或ID Token过期后,使用Refresh Token获取新的Access Token、ID Token信息。

Authorization Code模式代码示例:

1. 展示华为登录方式图标。

应用在登录页面展示帐号登录方式的图标,华为登录方式图标规范请参见华为帐号登录图标使用规范

2. 调用AccountAuthParamsHelper的 setAuthorizationCode方法请求授权。

Java

AccountAuthParams authParams = new AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM).setAuthorizationCode().createParams();

Kotlin

val authParams : AccountAuthParams = AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM).setAuthorizationCode().createParams()

3. 调用AccountAuthManager的 getService方法初始化AccountAuthService对象。

Java

AccountAuthService service = AccountAuthManager.getService(MainActivity.this, authParams);

Kotlin

val service : AccountAuthService = AccountAuthManager.getService(this@MainActivity, authParams)

4. 调用AccountAuthService的getSignInIntent方法并拉起帐号登录授权页面。

Java

startActivityForResult(service.getSignInIntent(), 8888);

Kotlin

startActivityForResult(service.signInIntent, 8888)

5. 登录授权完成后处理登录结果。

Java

@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { //授权登录结果处理,从AuthAccount中获取Authorization Code super.onActivityResult(requestCode, resultCode, data); if (requestCode == 8888) { Task<AuthAccount> authAccountTask = AccountAuthManager.parseAuthResultFromIntent(data); if (authAccountTask.isSuccessful()) { //登录成功,获取用户的帐号信息和Authorization Code AuthAccount authAccount = authAccountTask.getResult(); Log.i(TAG, "serverAuthCode:" + authAccount.getAuthorizationCode()); } else { //登录失败 Log.e(TAG, "sign in failed : " + ((ApiException)authAccountTask.getException()).getStatusCode()); } } }

Kotlin

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { //授权登录结果处理,从AuthAccount中获取Authorization Code super.onActivityResult(requestCode, resultCode, data) if (requestCode == 8888) { val authAccountTask = AccountAuthManager.parseAuthResultFromIntent(data) if (authAccountTask.isSuccessful) { //登录成功,获取用户的帐号信息和Authorization Code val authAccount = authAccountTask.result Log.i(TAG, "serverAuthCode:" + authAccount.authorizationCode) } else { //登录失败 Log.e(TAG, "sign in failed : " + (authAccountTask.exception as ApiException).statusCode) } } }

6. 应用客户端将获取到的code上报给应用服务端,再由其调用/oauth2/v3/token接口向帐号服务器请求获取ID Token、Access Token、Refresh Token。

请求样例:

POST /oauth2/v3/token HTTP/1.1 Host: oauth-login.cloud.huawei.com Content-Type: application/x-www-form-urlencoded grant_type=authorization_code& code=CF3L7XyCVZi52XMdsUzD7Z6ap0/N2qExcNe0AMqTselTtNd1B4DUwTsQ/23FPZasC8yI29v+N2s2jMT/T2MXiuc+178I/sYuWVoTyqwBaDqVW82KCMqaxbeWBguH4hEENxmDSUIE61Qg5R1F074PiS+qJYnbLI2IBqatS37px8pn5qnuq5oX+UX8XN3/w8HLt4GpakW5Dk1v7hGs& client_id={app_id}& client_secret={app_secret}& redirect_uri=https%3A%2F%2F/www.example.com/%2Fredirect_uri

返回样例:

HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "access_token": "CFyJ21sNODl16eV9y2vu3CwQk9DBr32BkOcxxgAd7MZUR5th1giyTk5\/kA+QDAyxou+\/5U2zzBRcf3qgLkkFdtbbC+mM3zFV7xj7CCEMHc5Tw92al0Y=", "refresh_token": "CF13G0sRaGybtYt7SIyeUILNORtTFwMgz4ao5C7j7vtgLPt6ogmXKjdI8RS\/YlyS71z4DyP6kEMnOrRlmNK0KhdOUNWd+qVLLRsEEHkqRIKpuAkPvL8=", "expires_in": 3600, "id_token": "eyJraWQiOiI3YTNlYjRkNTJmMDdhODM0NDU4MmRhOGQ3MWE1MGQ5MDlmNWM0YmRiZTFkNDQ3MjQ2MDNhZTA2NGM0ZTlkZGYyIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJhdF9oYXNoIjoiM0hPdFZYOEdMcG1GSDBWRVlSc1BjdyIsImF1ZCI6IjEwMDczNTE2NyIsInN1YiI6Ik1ERTlYaWFoc3MwaWFFNXU2c09PaEY5Mlhvell0Rkt4bUdtbWlhNGtTaEJ3dklLR2ciLCJhenAiOiIxMDA3MzUxNjciLCJpc3MiOiJodHRwczovL2FjY291bnRzLmh1YXdlaS5jb20iLCJuYW1lIjoi6Jab5oyv5Y2OIiwiZXhwIjoxNTczMDQ2NDI4LCJnaXZlbl9uYW1lIjoi6Jab5oyv5Y2OIiwiZGlzcGxheV9uYW1lIjoi5rKh5pyJ562U5qGIIiwiaWF0IjoxNTczMDQyODI4LCJwaWN0dXJlIjoiaHR0cHM6Ly91cGZpbGUtZHJjbi5wbGF0Zm9ybS5oaWNsb3VkLmNvbS9GaWxlU2VydmVyL2ltYWdlL2IuMDI2MDA4NjAwMDIzMjQ3MjUxMS4yMDE5MDgyMDExNTQ0Mi5tbmRjWTZyN2JUT0xNcVdiNVBhZDIzZExWNXh0b1Z2WC4xMDAwLkI0QkUyQTdEM0I3NkFGMzBCMkJDNjlBQ0JFNjg3NDIxMTQwMjhEQzYwREZFOTVCMjM5QkI0QzM2OUQwOUVEMkEuanBnIn0.mqy2C3ZNYEM8FKt8r1LX0VFosJjpqVl7E7mw2N-uEhnmAJq3blBco8fp2TCEyUzi1qFMN7-cjv87mQqCEpgfozyU7xV0VXMGdcd9ZhOxtabZtQGxUXRpIPiK5iysp68d95_QJAf2YZIdA4P_1zU8ZGxH57njIXRUVdQWDB8poeuB9gOc72bufe3DmSkqYD9aKvcibpA44Iln58aj-I9xs-FpcDwE6Y9hTfLGT5vk_5hXs32qwt54kEH1JjKbzZRW7B-OaELJIzzOM49oZKrdkViG6c2Tco1xX1WcKSz298Wckj4suLBAqkam4AprQgoSETC__ORTfy9OHIS1m4_8uQ", "scope": "openid profile email", "token_type": "Bearer" }

由于Access Token的有效期(目前是60分钟)较短, 当Access Token失效, 或者即将失效时,可以使用Refresh Token通过/oauth2/v3/token 接口向帐号服务器请求获取新的Access Token。

请求样例:

POST /oauth2/v3/token HTTP/1.1 Host: oauth-login.cloud.huawei.com Content-Type: application/x-www-form-urlencoded grant_type=refresh_token& client_id=12345& client_secret=bKaZ0VE3EYrXaXCdCe3d2k9few& refresh_token=CF2Mm03n0aos9iZZ8nIhfyDtoXy74CXeBi50gVVhMpB0IUzlv9ZwizEvTBhVoF820ZPim0JwNR9j2p1qgEQWnIVYZRlp4T6ezMgekUnsHBkvNev5rd2MdfQMLP

返回样例:

HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "access_token": "CFyJ4J\/l6wuwcFqYOJG4maq2ca8RAV+g0i+mel6qCV5lvqH0PYtW0+BNwfHWg0AqMnW6ZdBvUgs7ijkxMFh1xVP\/B+vQXz3PWsivkKCuL78XtbLt7vs=", "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjExOGRmMjU0YjgzNzE4OWQxYmMyYmU5NjUwYTgyMTEyYzAwZGY1YTQiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI3ODI0NTY2Njc4OTgtc2M0MzE3Y2l0NGEwMjB0NzdrbGdsbWo1ZjA4YWtnMWIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI3ODI0NTY2Njc4OTgtN2NkNGJpYWRkaGVwNGc4cnZic2VlOGtwcDA5Zm1hNzIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMDE3MTIxMzkwMzgwNDE2MDc0MTQiLCJlbWFpbCI6Inh1ZXpoZW5odWF0anVAc2luYS5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwicGljdHVyZSI6Imh0dHBzOi8vbGg1Lmdvb2dsZXVzZXJjb250ZW50LmNvbS8tMm9lTTllT09zNTAvQUFBQUFBQUFBQUkvQUFBQUFBQUFBQkkvMVpOSC0xdmxxc3cvczk2LWMvcGhvdG8uanBnIiwiaWF0IjoxNTYxNDUxMTUyLCJleHAiOjE1NjE0NTQ3NTJ9.Eo9IHMkid596jvt1YYzNsRtDq9c9K9dbougkU41Noh7TXNiko86_RuWwHID6k1kDg398AwC3wwH-t2hLcUjgrXPNd9XYU96Jp4-UxdDszP6ywEJgvvBCyTHzsi2auvKt_MnfSrs3qOKfh7noJvXq8AY-Hi3vqSUks5kGqbZKVzCHhBDO3RD9Fs9YHsB6w0XVKZojPOBDaAT_TiijoChn-Q-e8NbSGUx52OgeH-Nw5lOj6JVb_7fb6ucWRzlhiQuzFjklevLVw2pjw1MxKbl1vfRp0X699uZBVjgl9hj1L7LSDObuPzLiXF7ojji5JKYC6zIwAtZQUZ_VUmSk01GDLQ", "expires_in": 3600, "scope": "openid profile email", "token_type": "Bearer" }

静默登录

静默登录整体流程:

  1. 用户进行了触发静默登录的场景,根据应用实际场景由您自行设定。
  2. 应用客户端调用AccountAuthParamsHelper的默认构造方法配置鉴权参数。
  3. 帐号SDK向应用客户端返回包含授权参数的AccountAuthParams对象。
  4. 应用客户端调用AccountAuthManager.getService方法初始化AccountAuthService对象。
  5. 帐号SDK向应用客户端返回AccountAuthService对象。
  6. 应用客户端调用AccountAuthService.silentSignIn方法向帐号SDK发起静默登录请求。
  7. 帐号SDK检查用户是否符合静默登录的授权,并向应用客户端返回授权结果。
  8. 应用根据授权结果自行确定后续处理。

静默登录代码示例:

1. 调用AccountAuthParamsHelper的默认构造方法配置授权参数。

Java

AccountAuthParams authParams = new AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM).createParams();

Kotlin

val authParams : AccountAuthParams = AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM).createParams()

2. 调用AccountAuthManager的getService方法初始化AccountAuthService对象。

Java

AccountAuthService service = AccountAuthManager.getService(MainActivity.this, authParams);

Kotlin

val service : AccountAuthService = AccountAuthManager.getService(this@MainActivity, authParams)

3. 调用AccountAuthService.silentSignIn方法发起静默登录请求。

Java

Task<AuthAccount> task = service.silentSignIn();

Kotlin

val task : Task<AuthHuaweiId> = service.silentSignIn()

4. 处理授权结果。如果授权成功,可获取用户的帐号信息,允许用户登录。应用可根据authAccount.getAccountFlag的结果判断当前登录帐号的渠道类型。

Java

task.addOnSuccessListener(new OnSuccessListener<AuthAccount>() { @Override public void onSuccess(AuthAccount authAccount) { //获取帐号信息 Log.i(TAG, "displayName:" + authAccount.getDisplayName()); //获取帐号类型,0表示华为帐号、1表示AppTouch帐号 Log.i(TAG, "accountFlag:" + authAccount.getAccountFlag()); } });

Kotlin

task.addOnSuccessListener { authAccount -> //获取帐号信息 Log.i(TAG, "displayName:" + authAccount.displayName) //获取帐号类型,0表示华为帐号、1表示AppTouch帐号 Log.i(TAG, "accountFlag:" + authAccount.accountFlag); }

如果授权失败,可能是用户之前未进行过登录授权,应用可自行确认是否需要调用AccountAuthService的getSignInIntent方法显式展示登录授权页面。

Java

task.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { //登录失败,您可以尝试使用getSignInIntent()方法显式登录 if (e instanceof ApiException) { ApiException apiException = (ApiException)e; Log.i(TAG, "sign failed status:" + apiException.getStatusCode()); } } });

Kotlin

task.addOnFailureListener { e -> //登录失败,您可以尝试使用getSignInIntent()方法显式登录 if (e is ApiException) { Log.i(TAG, "sign failed status:" + e.statusCode) } }

退出帐号

退出帐号整体流程:

  1. 用户在已经登录应用,在应用中执行退出操作。
  2. 应用调用AccountAuthService.signOut方法向帐号SDK请求退出帐号。
  3. 帐号SDK清除帐号登录信息后,向应用返回退出结果。

退出帐号代码示例:

1. 使用帐号登录授权时创建的AccountAuthService实例调用signOut接口。

Java

//service为登录授权时使用getService方法生成的AccountAuthService实例 Task<Void> signOutTask = service.signOut();

Kotlin

//service为登录授权时使用getService方法生成的AccountAuthService实例 val signOutTask = service.signOut()

2. signOut完成后的处理。

Java

signOutTask.addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(Task<Void> task) { //完成退出后的处理 Log.i(TAG, "signOut complete"); } });

Kotlin

signOutTask.addOnCompleteListener { it -> //完成退出后的处理 Log.i(TAG, "signOut complete") }

帐号取消授权

帐号取消登录整体流程:

  1. 用户在已经登录应用并授权,在应用中执行取消授权。

应用客户端调用AccountAuthService.cancelAuthorization方法向帐号SDK请求取消授权。

帐号SDK清理帐号授权信息后,向应用客户端返回取消结果。

帐号取消授权代码示例:

1. 应用设置取消授权入口,此部分由应用自行开发。

2. 应用调用AccountAuthService.cancelAuthorization方法,并处理返回结果。

Java

//service为登录授权时使用getService方法生成的AccountAuthService实例 service.cancelAuthorization().addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(Task<Void> task) { if (task.isSuccessful()) { //取消授权成功后的处理 Log.i(TAG, "onSuccess: "); } else { //异常处理 Exception exception = task.getException(); if (exception instanceof ApiException){ int statusCode = ((ApiException) exception).getStatusCode(); Log.i(TAG, "onFailure: " + statusCode); } } } });

Kotlin

//service为登录授权时使用getService方法生成的AccountAuthService实例 service.cancelAuthorization().addOnCompleteListener { task -> if (task.isSuccessful) { //取消授权成功后的处理 Log.i(TAG, "onSuccess: ") } else { //异常处理 val exception = task.exception if (exception is ApiException) { val statusCode = exception.statusCode Log.i(TAG, "onFailure: $statusCode") } } }

开发完成后,点击图标 ,运行Android Studio工程打包生成APK,并安装在测试手机上。运行界面如下图所示:

干得好,您已经成功完成了Codelab并学到了:

帐号服务相关API介绍

您可以点击下方按钮下载源码。

源码下载

已复制代码