HarmonyOS Sample 之 JavaDistributeAuthDemo分布式身份认证功能 原创 精华

Buty9147
发布于 2021-12-9 01:29
浏览
3收藏

本文正在参与优质创作者激励
@toc

HarmonyOS Sample 之 JavaDistributeAuthDemo 分布式身份认证

1.介绍

相信大部分关注HarmonyOS的人来说,对于HarmonyOS的特性都有一定的了解了,从官网我们可以看到一些关键的提炼:“统一OS,弹性部署”,“硬件互助,资源共享”,“一次开发,多端部署
接下来几期就想和大家一起就HarmonyOS的特性,来找一些案例进行学习和实践,目的是进一步巩固对特性的理解然后去灵活应用。

这一期是通过分布式身份认证的功能来了解一下 常用的通信方法
分享的内容:
1.在设备迁移或协同时都需要显示可用设备列表,有一种方式不需要自己单独获取设备,也不需要自己定义列表布局文件就可以显示设备窗口。

2.如何实现一个分布式身份认证授权的功能。

案例来自codelabs官方示例分布式鉴权(Java) 本贴进行了整理和分析,供学习和交流使用。

2.效果展示

HarmonyOS Sample 之 JavaDistributeAuthDemo分布式身份认证功能-鸿蒙开发者社区

3.搭建环境

安装DevEco Studio,详情请参考DevEco Studio下载
设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:

如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境
下载源码后,使用DevEco Studio 打开项目,模拟器运行即可。
真机上运行,参见真机运行应用

4.项目结构

HarmonyOS Sample 之 JavaDistributeAuthDemo分布式身份认证功能-鸿蒙开发者社区

5.代码讲解

5.1 一种显示流转设备列表的方法

这种方式不需要自己单独获取设备,也不需要定义对应的布局文件就可以显示设备窗口。

①向ContinuationRegisterManager注册一个跳转的能力,并获得分配给该能力的注册令牌

/**
 * 注册流转能力
 * register Continuation
 *
 * @param context
 * @param deviceCallback
 * @param show           是否显示可用流转设备
 */
public void registerContinuation(AbilitySlice context, DeviceCallback deviceCallback, boolean show) {
    LogUtils.info("registerContinuation");
    if (continuationRegisterManager == null) {
        this.deviceCallback = deviceCallback;
        this.show = show;
        continuationRegisterManager = context.getContinuationRegisterManager();

        //支持的设备类型
        ExtraParams params = new ExtraParams();
        String[] devTypes = new String[]{ExtraParams.DEVICETYPE_SMART_PAD,
                ExtraParams.DEVICETYPE_SMART_WATCH,
                ExtraParams.DEVICETYPE_SMART_PHONE};
        params.setDevType(devTypes);

        //向ContinuationRegisterManager注册一个跳转的能力,并获得分配给该能力的注册令牌
        //您可以使用 IContinuationDeviceCallback 来监听用户选择设备进行能力跳跃后的设备连接状态变化,并实现您自己的处理逻辑。
        continuationRegisterManager.register(context.getBundleName(), params, callback, requestCallback);
    } else {
        if (show) {
            //显示设备列表
            showContinuationDevice();
        }
    }
}

②完成流转后的状态回调,提供用于侦听设备连接状态更改的回调

//完成流转后的状态回调,提供用于侦听设备连接状态更改的回调。
private IContinuationDeviceCallback callback = new IContinuationDeviceCallback() {
    @Override
    public void onDeviceConnectDone(String deviceId, String val) {
        LogUtils.info("onDeviceConnectDone");

        //设备连接完成后,提交选中设备的任务到队列,等同于点击了要流转的设备
        EventHandler eventHandler = new EventHandler(EventRunner.getMainEventRunner());
        //提交任务 到事件队列。
        eventHandler.postTask(new Runnable() {
            @Override
            public void run() {
                if (deviceCallback != null) {
                    deviceCallback.onItemClick(deviceId);
                }
                //更新指定能力成功跳转的设备的连接状态。
                continuationRegisterManager
                        .updateConnectStatus(abilityToken,
                                //表示需要更新连接状态的设备的ID。
                                deviceId,
                                DeviceConnectState.IDLE.getState(),null
                                );
            }
        });
    }

    @Override
    public void onDeviceDisconnectDone(String deviceId) {
        LogUtils.info("onDeviceDisconnectDone");
    }
};

③完成流转请求的回调,显示可流转的设备

//完成流转请求的回调,提供用于侦听跃点任务管理服务的连接状态变化的回调。
private RequestCallback requestCallback = new RequestCallback() {
    @Override
    public void onResult(int result) {
        abilityToken = result;
        if (show) {
            //显示 可流转设备
            showContinuationDevice();
        }
    }
};

/**
 * 显示 可流转设备
 * show Continuation
 */
private void showContinuationDevice() {
    LogUtils.info("showContinuation");

    ExtraParams extraParams = new ExtraParams();
    extraParams.setDevType(new String[]{ExtraParams.DEVICETYPE_SMART_TV,
            ExtraParams.DEVICETYPE_SMART_PAD,
            ExtraParams.DEVICETYPE_SMART_WATCH,
            ExtraParams.DEVICETYPE_SMART_PHONE});
    extraParams.setDescription("设备流转测试");

    //显示 可流转设备
    continuationRegisterManager.showDeviceList(abilityToken, extraParams, null);

}

5.2 实现一个分布式身份认证授权的功能

为了方便理解,把发送请求的设备成为 请求授权设备,进行授权操作的设备成为 授权设备。

RegisterManager 自定义了CommonEvent 接口,MainAbilitySlice实现了该接口,所以RegisterManager具备了 到 MainAbilitySlice方向的通信能力。

RegisterManager 完成了对 ConstUtil.ORDER_CODE 类型公共事件的订阅,所以就能够接收到该类型的公共事件。

认证授权的完整过程:

①在请求授权设备上,RegisterManager提供了注册设备流转能力的函数,在设备连接完成的状态回调中 提交了一个“点击设备”的任务到执行队列。

//完成流转后的状态回调,提供用于侦听设备连接状态更改的回调。
private IContinuationDeviceCallback callback = new IContinuationDeviceCallback() {
    @Override
    public void onDeviceConnectDone(String deviceId, String val) {
        LogUtils.info("onDeviceConnectDone");

        //设备连接完成后,提交选中设备的任务到队列,等同于点击了要流转的设备
        EventHandler eventHandler = new EventHandler(EventRunner.getMainEventRunner());
        //提交任务 到事件队列。
        eventHandler.postTask(new Runnable() {
            @Override
            public void run() {
                if (deviceCallback != null) {
                    deviceCallback.onItemClick(deviceId);
                }
                //更新指定能力成功跳转的设备的连接状态。
                continuationRegisterManager
                        .updateConnectStatus(abilityToken,
                                //表示需要更新连接状态的设备的ID。
                                deviceId,
                                DeviceConnectState.IDLE.getState(),null
                                );
            }
        });
    }

在MainAbilitySlice中,在完成流转能力注册完成后,在“点击设备” 的回调中,打开了远端授权设备上的AuthrRemoteSlice页,同时传递了ConstUtil.DEVICE_ID和ConstUtil.ORDER_CODE(ConstUtil.START_ORDER)参数过去,其中ConstUtil.START_ORDER并没有使用。

/**
 * 注册协同能力
 *
 * @param show
 */
private void registerContinuation(boolean show) {
    LogUtils.info("registerContinuation");

    registerManager.registerContinuation(this,
            new RegisterManager.DeviceCallback() {
                @Override
                public void onItemClick(String deviceId) {

                    LogUtils.info("onItemClick,deviceId:" + deviceId);

                    //启动远端Ablity
                    startRemoteAbility(deviceId);
                }
            }, show);
}

/**
 * 启动远端FA
 *
 * @param deviceId
 */
private void startRemoteAbility(String deviceId) {
    LogUtils.info("startRemoteAbility");

    DialogUtil.showToast(getContext(), "请求已经发送,等待对方确认。");
    //
    String localDeviceId = KvManagerFactory.getInstance().createKvManager(
            new KvManagerConfig(this)).getLocalDeviceInfo().getId();

    Intent intent = new Intent();
    Operation operation =
            new Intent.OperationBuilder()
                    .withDeviceId(deviceId)
                    .withBundleName(getBundleName())
                    .withAbilityName(MainAbility.class.getName())
                    //指向授权设备的认证页面路由
                    .withAction(MainAbility.ACTION)
                    .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
                    .build();
    intent.setOperation(operation);
    intent.setParam(ConstUtil.DEVICE_ID, localDeviceId);
    //启动远端FA指令代码
    intent.setParam(ConstUtil.ORDER_CODE, ConstUtil.START_ORDER);
    startAbility(intent);
}

②在授权设备上的AuthrRemoteSlice页被打开后,点击允许或不允许时,请求分布式权限后,又打开了请求授权设备的 MainAbility。

private void initViewData() {
    LogUtils.info("initViewData");

    findComponentById(ResourceTable.Id_yes_btn).setClickedListener(component -> {
        sendMessage(AUTH_TYPE1);
    });
    findComponentById(ResourceTable.Id_no_btn).setClickedListener(component -> {
        sendMessage(AUTH_TYPE2);
    });
}

private void sendMessage(int type) {
    LogUtils.info("sendMessage");

    //从MainAbility获取HPermission实例
    HPermission hPermission = ((MainAbility) getAbility()).getPermission();

    //如果用户已允许分布式权限,设置按钮可用
    hPermission.requestPermissions(this, () -> {
        // button Enabled
        findComponentById(ResourceTable.Id_yes_btn).setEnabled(false);
        findComponentById(ResourceTable.Id_no_btn).setEnabled(false);

        //打开请求侧的页面
        startRemoteAbility(type);
    });
}

/**
 * 打开请求授权侧的MainAbility
 *
 * @param type 是否同意授权
 */
private void startRemoteAbility(int type) {
    LogUtils.info("startRemoteAbility");

    DialogUtil.showToast(getContext(), type == AUTH_TYPE1 ? "允许玩游戏" : "已拒绝玩游戏");
    Intent intent = new Intent();
    Operation operation =
            new Intent.OperationBuilder()
                    .withDeviceId(remoteDeviceId == null ? "" : remoteDeviceId)
                    .withBundleName(getBundleName())
                    .withAbilityName(MainAbility.class.getName())
                    .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
                    .build();
    intent.setOperation(operation);

    //授权码
    intent.setParam(ORDER_CODE, type);
    startAbility(intent);

    //关闭当前宿主 Ability
    getUITaskDispatcher().delayDispatch(() -> terminateAbility(), DELAY);
}

③在请求授权设备上,由于MainAbility设置为singleton模式(“launchType”: “singleton”)而且已经实例过,所以请求进入到onNewIntent函数。
config.json

{
  ...
  "orientation": "unspecified",
  "visible": true,
  "name": "com.buty.javadistributedemo.MainAbility",
  "icon": "$media:icon",
  "description": "$string:mainability_description",
  "label": "$string:entry_MainAbility",
  "type": "page",
  "launchType": "singleton"
}

在onNewIntent函数中,通过 CommonEventManager发布一个ConstUtil.ORDER_CODE类型的事件,该事件被RegisterManager收到并进行了处理,如何处理的呢,又通过RegisterManager.CommonEvent 把事件传递给了实现了RegisterManager.CommonEvent接口MainAbilitySlice,最终显示对端设备的授权结果(允许/不允许)

/**
 * Ability设置为singleton模式
 * 当创建时,如果实例已存在,触发该函数
 *
 * @param intent
 */
@Override
protected void onNewIntent(Intent intent) {
    LogUtils.info("onNewIntent");
    super.onNewIntent(intent);
    //是否允许
    int code = intent.getIntParam(ConstUtil.ORDER_CODE, 0);
    //
    String deviceId = intent.getStringParam(ConstUtil.DEVICE_ID);
    sendCommonEvent(code, deviceId);
}

MainAbilitySlice收到消息

/**
 * 实现 RegisterManager的 CommonEvent接口
 *
 * @param code     code
 * @param deviceId deviceId
 */
@Override
public void onReceiveEvent(int code, String deviceId) {
    LogUtils.info("onReceiveEvent,code:"+code);
    switch (code) {
        // Agree to allow games to be played
        case ConstUtil.AUTH_TYPE1:
            //start.setVisibility(Component.HIDE);
            tips.setVisibility(Component.VISIBLE);
            tips.setText("已授权,可以开始游戏");

            break;
        // Refuse to play games
        case ConstUtil.AUTH_TYPE2:
            tips.setVisibility(Component.VISIBLE);
            //DialogUtil.exitDialog(getAbility());
            tips.setText("已拒绝,不可以游戏");
            break;
        default:
            break;
    }
}

6.思考总结

分布式中常用的通信方式:
1.Intent 直接传递参数(intent.setParam(ORDER_CODE, type))
2.公共事件订阅/发布的方式(Intent封装到CommonEventData)
3.自定义接口的方式(RegisterManager.CommonEvent)

7.完整代码

附件可以直接下载

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
JavaDistributeDemo.zip 1.42M 17次下载
已于2021-12-9 01:34:52修改
5
收藏 3
回复
举报
4条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

很经典的案例,感谢楼主整理分享。

回复
2021-12-9 10:26:02
mb61e428166ecd8
mb61e428166ecd8

讲解很详细,也很感谢资源分享

回复
2022-5-20 14:35:11
小威爱学习
小威爱学习

好贴值得反复学习

回复
2022-5-20 14:53:04
wx6245597465b5f
wx6245597465b5f

感谢分享

回复
2022-5-20 15:50:59
回复
    相关推荐