当前位置:首页 > 系统运维

HarmonyOS Sample之JavaDistributeAuthDemo分布式身份认证功能

想了解更多内容,之证功请访问:

和华为官方合作共建的分布份鸿蒙技术社区

https://harmonyos.51cto.com

1.介绍

相信大部分关注HarmonyOS的人来说,对于HarmonyOS的式身特性都有一定的了解了,从官网我们可以看到一些关键的之证功提炼:“统一OS,弹性部署”,分布份“硬件互助,式身资源共享”,之证功“一次开发,分布份多端部署”。式身

接下来几期就想和大家一起就HarmonyOS的之证功特性,来找一些案例进行学习和实践,分布份目的式身是进一步巩固对特性的理解然后去灵活应用。

这一期是之证功通过分布式身份认证的功能来了解一下 常用的通信方法。

分享的分布份内容:

在设备迁移或协同时都需要显示可用设备列表,有一种方式不需要自己单独获取设备,式身也不需要自己定义列表布局文件就可以显示设备窗口。 如何实现一个分布式身份认证授权的功能。

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

2.效果展示

3.搭建环境

安装DevEco Studio,服务器租用详情请参考DevEco Studio下载。

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

如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。

如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。

下载源码后,使用DevEco Studio 打开项目,模拟器运行即可。

真机上运行,参见真机运行应用

4.项目结构

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)

文章相关附件可以点击下面的原文链接前往下载

https://harmonyos.51cto.com/resource/1574

想了解更多内容,请访问:

和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

分享到:

滇ICP备2023006006号-16