1. 介绍
简介
HMS Core华为帐号注册用户量已达到10亿+,通过华为帐号可以一键登录应用,通过与华为帐号绑定,可以为应用快速引入新用户和登录。华为帐号开放遵循OAuth2.0以及OpenID Connect标准规范。
您需要:
- 在华为开发者联盟注册开发者帐号,并在AppGallery Connect上创建应用以及配置应用信息和开通华为帐号服务
- 使用Android Studio IDE创建Project,并配置接入华为帐号依赖的SDK
- 根据华为帐号服务开发指南,实现华为帐号开放服务的API调用以及调试
您将建立什么
在这个codelab中,您将使用已经创建好的Demo Project实现对华为帐号开放服务的API调用,通过Demo Project您可以体验到:
- 使用华为帐号授权登录的流程
- 获取华为帐号开放的用户基本信息(用户标识、头像、昵称等)
- 通过取消授权API接口取消应用的授权
您将会学到什么
- 如何在AppGallery Connect上创建应用
- 如何开通华为帐号服务
- 如何导入华为帐号服务的SDK
- 如何调用华为帐号服务的API接口
2. 您需要什么
硬件要求
- 开发计算机(台式机或笔记本电脑)。
- 预装了HMS Core(APK)4.0.0.300以上版本的HUAWEI手机一部,用于真机调试运行demo。
软件要求
- JDK 1.8及以上
- 安装Android Studio 3.X及以上
- minSdkVersion 17及以上
- targetSdkVersion 31(推荐)
- compileSdkVersion 31(推荐)
- Gradle 4.6及以上(推荐)
需要的知识点
Android基础开发能力
4. 能力接入准备
集成HUAWEI HMS Core能力需要完成以下准备工作:
- 创建AppGallery Connect应用
- 创建Android Studio工程
- 生成签名证书
- 生成签名证书指纹
- 配置签名证书指纹
- 保存配置文件
- 配置Maven仓地址及AGC gradle插件
- 在Android Studio配置签名文件
5. 开通帐号服务
- 在华为开发者联盟AppGallery Connect中选择"我的项目",在项目列表中选择创建的应用,在"项目设置"页面中选择"API管理"。
- 打开"华为帐号"服务开关。
至此,已经为创建的应用开通了"帐号"服务。
6. 集成HMS Core SDK
获取配置文件
- 打开AppGallery Connect应用管理中之前创建的应用,并选择"项目设置 > 常规",点击"默认数据处理位置"的设置按钮。
- 下载应用中的"agconnect-services.json"。
- 将 "agconnect-services.json"文件移至Android Studio开发工程应用模块的根目录。
配置HMS Core SDK依赖
- 打开应用级的build.gradle文件。
- 在"dependencies"中添加如下编译依赖,其中{version}替换为HMS Core SDK的版本号,例如:6.7.0.300。
dependencies { implementation 'com.huawei.hms:hwid:{version}' }
点击Sync Now同步工程。
配置HMS Core SDK的Maven仓地址
Android Studio的代码库配置在Gradle 插件7.0以下版本、7.0版本和7.1及以上版本有所不同。请根据您当前的Gradle 插件版本,选择对应的配置过程。
7.0以下版本 | 7.0版本 | 7.1及以上版本 |
7.0以下版本
a. 打开Android Studio项目级"build.gradle"文件。
b. 添加HUAWEI agcp插件以及Maven代码库。
- 在"buildscript > repositories"中配置HMS Core SDK的Maven仓地址。
- 在"allprojects > repositories"中配置HMS Core SDK的Maven仓地址。
- 如果App中添加了"agconnect-services.json"文件则需要在"buildscript > dependencies"中增加agcp插件配置。
buildscript { repositories { google() jcenter() // 配置HMS Core SDK的Maven仓地址。 maven {url 'https://developer.huawei.com/repo/'} } dependencies { ... // 增加agcp插件配置,推荐您使用最新版本的agcp插件。 classpath 'com.huawei.agconnect:agcp:1.6.0.300' } } allprojects { repositories { google() jcenter() // 配置HMS Core SDK的Maven仓地址。 maven {url 'https://developer.huawei.com/repo/'} } }
7.0版本
a. 打开Android Studio项目级"build.gradle"文件。
b. 添加HUAWEI agcp插件以及Maven代码库。
- 在"buildscript > repositories"中配置HMS Core SDK的Maven仓地址。
- 如果App中添加了"agconnect-services.json"文件则需要在"buildscript > dependencies"中增加agcp插件配置。
buildscript { repositories { google() jcenter() // 配置HMS Core SDK的Maven仓地址。 maven {url 'https://developer.huawei.com/repo/'} } dependencies { ... // 增加agcp插件配置,推荐您使用最新版本的agcp插件。 classpath 'com.huawei.agconnect:agcp:1.6.0.300' } }
c. 打开项目级"settings.gradle"文件,配置HMS Core SDK的Maven仓地址。
dependencyResolutionManagement { ... repositories { google() jcenter() // 配置HMS Core SDK的Maven仓地址。 maven {url 'https://developer.huawei.com/repo/'} } }
7.1及以上版本
a. 打开Android Studio项目级"build.gradle"文件。
b. 如果App中添加了"agconnect-services.json"文件则需要在"buildscript > dependencies"中增加agcp插件配置。
buildscript { dependencies { ... // 增加agcp插件配置,推荐您使用最新版本的agcp插件。 classpath 'com.huawei.agconnect:agcp:1.6.0.300' } }
c. 打开项目级"settings.gradle"文件,配置HMS Core SDK的Maven仓地址。
pluginManagement { repositories { gradlePluginPortal() google() mavenCentral() // 配置HMS Core SDK的Maven仓地址。 maven { url 'https://developer.huawei.com/repo/' } } } dependencyResolutionManagement { ... repositories { google() mavenCentral() // 配置HMS Core SDK的Maven仓地址。 maven { url 'https://developer.huawei.com/repo/' } } }
配置混淆脚本
打开Android Studio工程的混淆配置文件proguard-rules.pro。
加入混淆配置。
-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.**{*;}
- 如果您使用了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*"
添加权限
Account SDK需要获取网络状态权限和获取Wi-Fi状态权限。需要在Manifest文件中添加下面权限:
<!--check network permissions-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--check wifi state-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
7. 界面设计
帐号的交互接口主要涉及登录、静默登录、退出帐号、取消授权。本次codelab中您可以在Android Studio工程中创建一个布局页面,参照下图进行UI设计。
8. 开发帐号授权功能
ID Token模式登录帐号
整体流程
- 用户点击帐号授权登录图标,请求使用帐号授权登录。
- 帐号拉起用户登录授权界面,界面上会根据登录请求中携带的授权域(scopes)信息,显示告知用户需要授权的内容。
- 用户手动允许授权后,帐号返回ID Token信息。
- 应用选择使用本地方式验证或帐号服务器方式验证ID Token。
代码示例
- 展示华为登录方式图标。
应用在登录页面展示帐号登录方式的图标,华为登录方式图标规范请参见华为帐号登录图标使用规范。 调用AccountAuthParamsHelper的setIdToken方法请求授权。
JavaAccountAuthParams authParams = new AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM).setIdToken().createParams();
Kotlin
val authParams : AccountAuthParams = AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM).setIdToken().createParams()
- 调用AccountAuthManager的 getService方法初始化AccountAuthService对象。
JavaAccountAuthService service = AccountAuthManager.getService(MainActivity.this, authParams);
Kotlin
val service : AccountAuthService = AccountAuthManager.getService(this@MainActivity, authParams)
- 调用AccountAuthService的getSignInIntent方法并拉起帐号登录授权页面。
JavastartActivityForResult(service.getSignInIntent(), 8888);
Kotlin
startActivityForResult(service.signInIntent, 8888)
- 登录授权完成后处理登录结果。
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端的应用授权。
整体流程
- 用户点击帐号授权登录图标,请求使用帐号授权登录。
- 帐号拉起用户授权界面,界面上会根据登录请求中携带的授权域(scopes)信息, 显示告知用户需要授权的内容。
- 用户手动允许授权后,帐号返回Authorization Code信息。
- 第三方应用获取Authorization Code后,通过服务端接口获取Access Token、Refresh Token、Id Token信息。
- Access Token或ID Token过期后,使用Refresh Token获取新的Access Token、ID Token信息。
代码示例
- 展示华为登录方式图标。
应用在登录页面展示帐号登录方式的图标,华为登录方式图标规范请参见华为帐号登录图标使用规范。 调用AccountAuthParamsHelper的 setAuthorizationCode方法请求授权。
JavaAccountAuthParams authParams = new AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM).setAuthorizationCode().createParams();
Kotlin
val authParams : AccountAuthParams = AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM).setAuthorizationCode().createParams()
- 调用AccountAuthManager的 getService方法初始化AccountAuthService对象。
JavaAccountAuthService service = AccountAuthManager.getService(MainActivity.this, authParams);
Kotlin
val service : AccountAuthService = AccountAuthManager.getService(this@MainActivity, authParams)
- 调用AccountAuthService的getSignInIntent方法并拉起帐号登录授权页面。
JavastartActivityForResult(service.getSignInIntent(), 8888);
Kotlin
startActivityForResult(service.signInIntent, 8888)
- 登录授权完成后处理登录结果。
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) } } }
- 应用客户端将获取到的code上报给应用服务端,再由其调用/oauth2/v3/token接口向帐号服务器请求获取ID Token、Access Token、Refresh Token。
- 由于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" }
静默登录
整体流程
- 用户进行了触发静默登录的场景,根据应用实际场景由您自行设定。
- 应用客户端调用AccountAuthParamsHelper的默认构造方法配置鉴权参数。
- 帐号SDK向应用客户端返回包含授权参数的AccountAuthParams对象。
- 应用客户端调用AccountAuthManager.getService方法初始化AccountAuthService对象。
- 帐号SDK向应用客户端返回AccountAuthService对象。
- 应用客户端调用AccountAuthService.silentSignIn方法向帐号SDK发起静默登录请求。
- 帐号SDK检查用户是否符合静默登录的授权,并向应用客户端返回授权结果。
- 应用根据授权结果自行确定后续处理。
代码示例
- 调用AccountAuthParamsHelper的默认构造方法配置授权参数。
JavaAccountAuthParams authParams = new AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM).createParams();
Kotlin
val authParams : AccountAuthParams = AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM).createParams()
- 调用AccountAuthManager的getService方法初始化AccountAuthService对象。
JavaAccountAuthService service = AccountAuthManager.getService(MainActivity.this, authParams);
Kotlin
val service : AccountAuthService = AccountAuthManager.getService(this@MainActivity, authParams)
- 调用AccountAuthService.silentSignIn方法发起静默登录请求。
JavaTask<AuthAccount> task = service.silentSignIn();
Kotlin
val task : Task<AuthAccount> = service.silentSignIn()
- 处理授权结果。如果授权成功,可获取用户的帐号信息,允许用户登录。应用可根据authAccount.getAccountFlag的结果判断当前登录帐号的渠道类型。
Javatask.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方法显式展示登录授权页面。
Javatask.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) } }
退出帐号
整体流程
- 用户在已经登录应用,在应用中执行退出操作。
- 应用调用AccountAuthService.signOut方法向帐号SDK请求退出帐号。
- 帐号SDK清除帐号登录信息后,向应用返回退出结果。
代码示例
- 使用帐号登录授权时创建的AccountAuthService实例调用signOut接口。
Java//service为登录授权时使用getService方法生成的AccountAuthService实例 Task<Void> signOutTask = service.signOut();
Kotlin
//service为登录授权时使用getService方法生成的AccountAuthService实例 val signOutTask = service.signOut()
- signOut完成后的处理。
JavasignOutTask.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") }
帐号取消授权
整体流程
- 用户在已经登录应用并授权,在应用中执行取消授权。
- 应用客户端调用AccountAuthService.cancelAuthorization方法向帐号SDK请求取消授权。
- 帐号SDK清理帐号授权信息后,向应用客户端返回取消结果。
代码示例
- 应用设置取消授权入口,此部分由应用自行开发。
- 应用调用AccountAuthService.cancelAuthorization方法,并处理返回结果。
Java//service为登录授权时使用getService方法生成的AccountAuthService实例 service.cancelAuthorization().addOnCompleteListener(new OnCompleteListener
() { @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") } } }
9. 打包测试
开发完成后,点击图标,运行Android Studio工程打包生成APK,并安装在测试手机上。运行界面如下图所示: