近些年来,随着手机技术迭代更新越来越快,用户更换手机的周期也在缩短,在这样的背景下,开发者不得不面临以下问题:
同一开发者旗下常常有多个安卓应用和多形态应用(快应用和Web应用),用户更换一个新的设备(手机或平板)后,在新设备上登录各应用时每次都需要重复输入帐号和密码,导致用户在登录阶段流失率增加,同时开发者还需要承担额外的短信成本(如用户使用短信登录)。
华为HMS Core钥匙环服务(Keyring)提供凭据管理接口(Credentials Management API),为Android手机、平板提供用户登录凭据存储和跨应用、跨应用形态、跨设备共享的能力。
钥匙环服务提供了Android API、Web API和快应用API,应用程序通过调用这些API来使用钥匙环服务。无论调用哪种形式的接口,所有的用户凭据最终都会存储在HMS Core的钥匙环服务中,以便实现统一的凭据管理能力和共享能力。
钥匙环服务提供登录凭据本地储存和跨形态、跨应用共享能力。钥匙环服务将用户登录凭据加密储存在本地设备,被保存的凭据通过钥匙环服务共享至同一开发者旗下的其他快应用、Web应用和安卓应用;实现跨形态、跨应用无缝登录体验。
钥匙环服务使用端到端加密同步技术实现登录凭据跨设备同步能力。用户在新老设备上打开“凭据多设备同步”功能,就可以在新设备上免密登录同一开发者旗下的各形态应用,实现跨设备无缝登录体验。
例如,同一开发者将旗下的两个安卓应用和两个快应用接入钥匙环服务后,用户只需要在手机A和手机B上打开“凭据多设备同步”功能,手机A上登录一个应用后,用手机B登录时无需再输入帐号和密码,实现跨设备、跨应用、跨形态的无缝登录体验。
打造无缝登录体验
通过钥匙环服务接口获取本地存储的用户凭据,实现便捷登录。
保障数据安全可靠
用户登录凭据在设备内加密存储,在设备间通过端到端加密技术同步,云端无法解密。
降低登录流失率
简化用户登录时操作流程,降低流失率
降低运营成本
减少使用短信登录,降低运营成本
开发准备
详细准备步骤可参考华为开发者联盟官网。
用户登录场景
1、使用一个Activity实例初始化CredentialClient,可以写在Activity的onCreate方法中。
1 | CredentialClient credentialClient = CredentialManager.getCredentialClient(this); |
2、查询是否存在可用的凭据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | List <AppIdentity> trustedAppList = new ArrayList<>(); trustedAppList.add(new AndroidAppIdentity( "yourAppName" , "yourAppPackageName" , "yourAppCodeSigningCertHash" )); trustedAppList.add(new WebAppIdentity( "youWebSiteName" , "www.yourdomain.com" )); trustedAppList.add(new WebAppIdentity( "youWebSiteName" , "login.yourdomain.com" )); SharedCredentialFilter sharedCredentialFilter = SharedCredentialFilter.acceptTrustedApps(trustedAppList); credentialClient.findCredential(sharedCredentialFilter, new CredentialCallback< List <Credential>>() { @Override public void onSuccess( List <Credential> credentials) { if (credentials.isEmpty()) { Toast.makeText(MainActivity.this, R.string.no_available_credential, Toast.LENGTH_SHORT).show(); } else { for (Credential credential : credentials) { } } } @Override public void onFailure( long errorCode, CharSequence description) { Toast.makeText(MainActivity.this, R.string.query_credential_failed, Toast.LENGTH_SHORT).show(); } }); |
3、调用Credential.getContent获取凭据内容,在CredentialCallback<T>获取结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | private Credential mCredential; / / 获取的凭据 mCredential.getContent(new CredentialCallback<byte[]>() { @Override public void onSuccess(byte[] bytes) { String hint = String. format (getResources().getString(R.string.get_password_ok), new String(bytes)); Toast.makeText(MainActivity.this, hint, Toast.LENGTH_SHORT).show(); mResult.setText(new String(bytes)); } @Override public void onFailure( long l, CharSequence charSequence) { Toast.makeText(MainActivity.this, R.string.get_password_failed, Toast.LENGTH_SHORT).show(); mResult.setText(R.string.get_password_failed); } }); |
4、用户输入了新凭据,调用凭据存储接口。
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 | AndroidAppIdentity app2 = new AndroidAppIdentity(sharedToAppName, sharedToAppPackage, sharedToAppCertHash); List <AppIdentity> sharedAppList = new ArrayList<>(); sharedAppList.add(app2); Credential credential = new Credential(username, CredentialType.PASSWORD, userAuth, password.getBytes()); credential.setDisplayName( "user_niceday" ); credential.setSharedWith(sharedAppList); credential.setSyncable(true); credentialClient.saveCredential(credential, new CredentialCallback<Void>() { @Override public void onSuccess(Void unused) { Toast.makeText(MainActivity.this, R.string.save_credential_ok, Toast.LENGTH_SHORT).show(); } @Override public void onFailure( long errorCode, CharSequence description) { Toast.makeText(MainActivity.this, R.string.save_credential_failed + " " + errorCode + ":" + description, Toast.LENGTH_SHORT).show(); } }); |
1、使用一个Activity实例初始化CredentialClient,可以写在Activity的onCreate方法中。
1 | CredentialClient credentialClient = CredentialManager.getCredentialClient(this); |
2、查询是否存在可用的凭据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | List <AppIdentity> trustedAppList = new ArrayList<>(); trustedAppList.add(new AndroidAppIdentity( "yourAppName" , "yourAppPackageName" , "yourAppCodeSigningCertHash" )); trustedAppList.add(new WebAppIdentity( "youWebSiteName" , "www.yourdomain.com" )); trustedAppList.add(new WebAppIdentity( "youWebSiteName" , "login.yourdomain.com" )); SharedCredentialFilter sharedCredentialFilter = SharedCredentialFilter.acceptTrustedApps(trustedAppList); credentialClient.findCredential(sharedCredentialFilter, new CredentialCallback< List <Credential>>() { @Override public void onSuccess( List <Credential> credentials) { if (credentials.isEmpty()) { Toast.makeText(MainActivity.this, R.string.no_available_credential, Toast.LENGTH_SHORT).show(); } else { for (Credential credential : credentials) { / / 可以对可用凭据进行进一步处理,包括:获取凭据相关信息、获取凭据内容、删除 } } } @Override public void onFailure( long errorCode, CharSequence description) { Toast.makeText(MainActivity.this, R.string.query_credential_failed, Toast.LENGTH_SHORT).show(); } }); |
3、调用deleteCredential删除凭据,在CredentialCallback获取结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | credentialClient.deleteCredential(credential, new CredentialCallback<Void>() { @Override public void onSuccess(Void unused) { String hint = String. format (getResources().getString(R.string.delete_ok), credential.getUsername()); Toast.makeText(MainActivity.this, hint, Toast.LENGTH_SHORT).show(); } @Override public void onFailure( long errorCode, CharSequence description) { String hint = String. format (getResources().getString(R.string.delete_failed), description); Toast.makeText(MainActivity.this, hint, Toast.LENGTH_SHORT).show(); } }); |
通过API参数实现凭据共享
在调用saveCredential保存凭据时,您可以通过setSharedWith设置Credential对象的属性实现凭据共享,最多支持共享给128个应用。
示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | AndroidAppIdentity app1 = new AndroidAppIdentity( "your android app name" , "your android app package name" , "3C:99:C3:...." ); QuickAppIdentity app2 = new QuickAppIdentity( "your quick app name" , "your quick app package name" , "DC:99:C4:...." ); List <AppIdentity> sharedAppList = new ArrayList<>(); / / 共享关系列表 sharedAppList.add(app1); sharedAppList.add(app2); Credential credential = new Credential( "username" , CredentialType.PASSWORD, true, "password" .getBytes()); credential.setSharedWith(sharedAppList); / / 设置共享关系 credentialClient.saveCredential(credential, new CredentialCallback<Void>() { @Override public void onSuccess(Void unused) { Toast.makeText(MainActivity.this, R.string.save_credential_ok, Toast.LENGTH_SHORT).show(); } @Override public void onFailure( long errorCode, CharSequence description) { Toast.makeText(MainActivity.this, R.string.save_credential_failed + " " + errorCode + ":" + description, Toast.LENGTH_SHORT).show(); } }); |
通过Digital Asset Links资源实现凭据共享
您可以在Android应用的AndroidManifest.xml中添加凭据共享关系,方法如下:
1、在AndroidManifest.xml的<application>节点中添加以下内容:
1 2 3 4 5 | <application> <meta - data android:name = "asset_statements" android:value = "@string/asset_statements" / > < / application> |
1 | <string name = "asset_statements" >your digital asset links statements< / string> |
Digital asset links statements是一个遵循Digital Asset links规范的JSON字符串,示例如下:
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 | [{ "relation" : [ "delegate_permission/common.get_login_creds" ], "target" : { "namespace" : "web" , "site" : "https://developer.huawei.com" / / 您的网站域名 } }, { "relation" : [ "delegate_permission/common.get_login_creds" ], "target" : { "namespace" : "android_app" , "package_name" : "your android app package name" , "sha256_cert_fingerprints" : [ "F2:52:4D:..." ] } }, { "relation" : [ "delegate_permission/common.get_login_creds" ], "target" : { "namespace" : "quick_app" , "package_name" : "your quick app package name" , "sha256_cert_fingerprints" : [ "C3:68:9F:..." ] } } ] |
relation属性的值固定为["delegate_permission/common.get_login_creds"],表示把凭据共享给target属性所描述的应用。
航班管家和高铁管家集成华为钥匙环服务,为两亿用户打造无缝登录体验。
了解更多详情>>
访问华为开发者联盟官网
获取开发指导文档
华为移动服务开源仓库地址:GitHub、Gitee
关注我们,第一时间了解 HMS Core 最新技术资讯~