• 钥匙环服务,打造无缝的跨设备登录体验


    近些年来,随着手机技术迭代更新越来越快,用户更换手机的周期也在缩短,在这样的背景下,开发者不得不面临以下问题:

    同一开发者旗下常常有多个安卓应用和多形态应用(快应用和Web应用),用户更换一个新的设备(手机或平板)后,在新设备上登录各应用时每次都需要重复输入帐号和密码,导致用户在登录阶段流失率增加,同时开发者还需要承担额外的短信成本(如用户使用短信登录)。

    华为HMS Core钥匙环服务(Keyring)提供凭据管理接口(Credentials Management API),为Android手机、平板提供用户登录凭据存储和跨应用、跨应用形态、跨设备共享的能力。

    钥匙环服务提供了Android APIWeb API快应用API,应用程序通过调用这些API来使用钥匙环服务。无论调用哪种形式的接口,所有的用户凭据最终都会存储在HMS Core的钥匙环服务中,以便实现统一的凭据管理能力和共享能力。

    一、功能特点

    钥匙环服务提供登录凭据本地储存和跨形态、跨应用共享能力。钥匙环服务将用户登录凭据加密储存在本地设备,被保存的凭据通过钥匙环服务共享至同一开发者旗下的其他快应用、Web应用和安卓应用;实现跨形态、跨应用无缝登录体验。

    钥匙环服务使用端到端加密同步技术实现登录凭据跨设备同步能力。用户在新老设备上打开“凭据多设备同步”功能,就可以在新设备上免密登录同一开发者旗下的各形态应用,实现跨设备无缝登录体验。

    例如,同一开发者将旗下的两个安卓应用和两个快应用接入钥匙环服务后,用户只需要在手机A和手机B上打开“凭据多设备同步”功能,手机A上登录一个应用后,用手机B登录时无需再输入帐号和密码,实现跨设备、跨应用、跨形态的无缝登录体验。

    二、接入优势

    打造无缝登录体验

    通过钥匙环服务接口获取本地存储的用户凭据,实现便捷登录。

    保障数据安全可靠

    用户登录凭据在设备内加密存储,在设备间通过端到端加密技术同步,云端无法解密。

    降低登录流失率

    简化用户登录时操作流程,降低流失率

    降低运营成本

    减少使用短信登录,降低运营成本

    三、开发步骤

    开发准备

    详细准备步骤可参考华为开发者联盟官网

    集成Keyring客户端

    用户登录场景

    1、使用一个Activity实例初始化CredentialClient,可以写在Activity的onCreate方法中。

    CredentialClient credentialClient = CredentialManager.getCredentialClient(this);
    

    2、查询是否存在可用的凭据。

    List 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>() {
        @Override
        public void onSuccess(List 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获取结果。

    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、用户输入了新凭据,调用凭据存储接口。

    AndroidAppIdentity app2 = new AndroidAppIdentity(sharedToAppName,
                    sharedToAppPackage, sharedToAppCertHash);
    List 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() {
        @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方法中。

    CredentialClient credentialClient = CredentialManager.getCredentialClient(this);
    

    2、查询是否存在可用的凭据。

    List 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>() {
        @Override
        public void onSuccess(List 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获取结果。

    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个应用。

    示例如下:

    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 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() {
        @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>
               <meta-data
                android:name="asset_statements"
                android:value="@string/asset_statements" />
    application>
    
    1. 在res\values\strings.xml中添加以下内容:
    <string name="asset_statements">your digital asset links statementsstring>
    

    Digital asset links statements是一个遵循Digital Asset links规范的JSON字符串,示例如下:

    [{
                       "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属性所描述的应用。

    案例分享

    航班管家和高铁管家集成华为钥匙环服务,为两亿用户打造无缝登录体验。

    了解更多详情>>

    访问华为开发者联盟官网
    获取开发指导文档
    华为移动服务开源仓库地址:GitHubGitee

    关注我们,第一时间了解 HMS Core 最新技术资讯~

  • 相关阅读:
    Pytorch的入门操作(三)
    记一次 .NET 某仪器测量系统 CPU爆高分析
    CVE-2022-40684 Fortinet多个产品身份验证绕过漏洞复现
    【matlab图像处理】直方图均衡化操作
    canvas基础3 -- 交互
    【API封装接口的应用】大数据值得深思的十二个典型应用案例,和未来机遇畅想
    OCTAFX滑点滑到令人发指 现在出金也不给出金是黑平台无疑了
    1.3优先级
    1080 Graduate Admission
    不好意思,Nginx 该换了!
  • 原文地址:https://www.cnblogs.com/hmscore/p/17272541.html