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

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

[[439852]]

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

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

https://harmonyos.51cto.com

1.介绍

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

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

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

分享的内容:

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

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

2.效果展示

HarmonyOS Sample 之 JavaDistributeAuthDemo分布式身份认证功能-鸿蒙HarmonyOS技术社区

3.搭建环境

安装DevEco Studio,详情请参考DevEco Studio下载

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

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

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

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

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

4.项目结构

HarmonyOS Sample 之 JavaDistributeAuthDemo分布式身份认证功能-鸿蒙HarmonyOS技术社区

5.代码讲解

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

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

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

  1. /** 
  2.  * 注册流转能力 
  3.  * register Continuation 
  4.  * 
  5.  * @param context 
  6.  * @param deviceCallback 
  7.  * @param show           是否显示可用流转设备 
  8.  */ 
  9. public void registerContinuation(AbilitySlice context, DeviceCallback deviceCallback, boolean show) { 
  10.     LogUtils.info("registerContinuation"); 
  11.     if (continuationRegisterManager == null) { 
  12.         this.deviceCallback = deviceCallback; 
  13.         this.show = show; 
  14.         continuationRegisterManager = context.getContinuationRegisterManager(); 
  15.  
  16.         //支持的设备类型 
  17.         ExtraParams params = new ExtraParams(); 
  18.         String[] devTypes = new String[]{ExtraParams.DEVICETYPE_SMART_PAD, 
  19.                 ExtraParams.DEVICETYPE_SMART_WATCH, 
  20.                 ExtraParams.DEVICETYPE_SMART_PHONE}; 
  21.         params.setDevType(devTypes); 
  22.  
  23.         //向ContinuationRegisterManager注册一个跳转的能力,并获得分配给该能力的注册令牌 
  24.         //您可以使用 IContinuationDeviceCallback 来监听用户选择设备进行能力跳跃后的设备连接状态变化,并实现您自己的处理逻辑。 
  25.         continuationRegisterManager.register(context.getBundleName(), params, callback, requestCallback); 
  26.     } else { 
  27.         if (show) { 
  28.             //显示设备列表 
  29.             showContinuationDevice(); 
  30.         } 
  31.     } 

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

  1. //完成流转后的状态回调,提供用于侦听设备连接状态更改的回调。 
  2. private IContinuationDeviceCallback callback = new IContinuationDeviceCallback() { 
  3.     @Override 
  4.     public void onDeviceConnectDone(String deviceId, String val) { 
  5.         LogUtils.info("onDeviceConnectDone"); 
  6.  
  7.         //设备连接完成后,提交选中设备的任务到队列,等同于点击了要流转的设备 
  8.         EventHandler eventHandler = new EventHandler(EventRunner.getMainEventRunner()); 
  9.         //提交任务 到事件队列。 
  10.         eventHandler.postTask(new Runnable() { 
  11.             @Override 
  12.             public void run() { 
  13.                 if (deviceCallback != null) { 
  14.                     deviceCallback.onItemClick(deviceId); 
  15.                 } 
  16.                 //更新指定能力成功跳转的设备的连接状态。 
  17.                 continuationRegisterManager 
  18.                         .updateConnectStatus(abilityToken, 
  19.                                 //表示需要更新连接状态的设备的ID。 
  20.                                 deviceId, 
  21.                                 DeviceConnectState.IDLE.getState(),null 
  22.                                 ); 
  23.             } 
  24.         }); 
  25.     } 
  26.  
  27.     @Override 
  28.     public void onDeviceDisconnectDone(String deviceId) { 
  29.         LogUtils.info("onDeviceDisconnectDone"); 
  30.     } 
  31. }; 

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

  1. //完成流转请求的回调,提供用于侦听跃点任务管理服务的连接状态变化的回调。 
  2. private RequestCallback requestCallback = new RequestCallback() { 
  3.     @Override 
  4.     public void onResult(int result) { 
  5.         abilityToken = result; 
  6.         if (show) { 
  7.             //显示 可流转设备 
  8.             showContinuationDevice(); 
  9.         } 
  10.     } 
  11. }; 
  12.  
  13. /** 
  14.  * 显示 可流转设备 
  15.  * show Continuation 
  16.  */ 
  17. private void showContinuationDevice() { 
  18.     LogUtils.info("showContinuation"); 
  19.  
  20.     ExtraParams extraParams = new ExtraParams(); 
  21.     extraParams.setDevType(new String[]{ExtraParams.DEVICETYPE_SMART_TV, 
  22.             ExtraParams.DEVICETYPE_SMART_PAD, 
  23.             ExtraParams.DEVICETYPE_SMART_WATCH, 
  24.             ExtraParams.DEVICETYPE_SMART_PHONE}); 
  25.     extraParams.setDescription("设备流转测试"); 
  26.  
  27.     //显示 可流转设备 
  28.     continuationRegisterManager.showDeviceList(abilityToken, extraParams, null); 
  29.  

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

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

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

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

认证授权的完整过程:

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

  1. //完成流转后的状态回调,提供用于侦听设备连接状态更改的回调。 
  2. private IContinuationDeviceCallback callback = new IContinuationDeviceCallback() { 
  3.     @Override 
  4.     public void onDeviceConnectDone(String deviceId, String val) { 
  5.         LogUtils.info("onDeviceConnectDone"); 
  6.  
  7.         //设备连接完成后,提交选中设备的任务到队列,等同于点击了要流转的设备 
  8.         EventHandler eventHandler = new EventHandler(EventRunner.getMainEventRunner()); 
  9.         //提交任务 到事件队列。 
  10.         eventHandler.postTask(new Runnable() { 
  11.             @Override 
  12.             public void run() { 
  13.                 if (deviceCallback != null) { 
  14.                     deviceCallback.onItemClick(deviceId); 
  15.                 } 
  16.                 //更新指定能力成功跳转的设备的连接状态。 
  17.                 continuationRegisterManager 
  18.                         .updateConnectStatus(abilityToken, 
  19.                                 //表示需要更新连接状态的设备的ID。 
  20.                                 deviceId, 
  21.                                 DeviceConnectState.IDLE.getState(),null 
  22.                                 ); 
  23.             } 
  24.         }); 
  25.     } 

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

  1. /** 
  2.  * 注册协同能力 
  3.  * 
  4.  * @param show 
  5.  */ 
  6. private void registerContinuation(boolean show) { 
  7.     LogUtils.info("registerContinuation"); 
  8.  
  9.     registerManager.registerContinuation(this, 
  10.             new RegisterManager.DeviceCallback() { 
  11.                 @Override 
  12.                 public void onItemClick(String deviceId) { 
  13.  
  14.                     LogUtils.info("onItemClick,deviceId:" + deviceId); 
  15.  
  16.                     //启动远端Ablity 
  17.                     startRemoteAbility(deviceId); 
  18.                 } 
  19.             }, show); 
  20.  
  21. /** 
  22.  * 启动远端FA 
  23.  * 
  24.  * @param deviceId 
  25.  */ 
  26. private void startRemoteAbility(String deviceId) { 
  27.     LogUtils.info("startRemoteAbility"); 
  28.  
  29.     DialogUtil.showToast(getContext(), "请求已经发送,等待对方确认。"); 
  30.     // 
  31.     String localDeviceId = KvManagerFactory.getInstance().createKvManager( 
  32.             new KvManagerConfig(this)).getLocalDeviceInfo().getId(); 
  33.  
  34.     Intent intent = new Intent(); 
  35.     Operation operation = 
  36.             new Intent.OperationBuilder() 
  37.                     .withDeviceId(deviceId) 
  38.                     .withBundleName(getBundleName()) 
  39.                     .withAbilityName(MainAbility.class.getName()) 
  40.                     //指向授权设备的认证页面路由 
  41.                     .withAction(MainAbility.ACTION
  42.                     .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) 
  43.                     .build(); 
  44.     intent.setOperation(operation); 
  45.     intent.setParam(ConstUtil.DEVICE_ID, localDeviceId); 
  46.     //启动远端FA指令代码 
  47.     intent.setParam(ConstUtil.ORDER_CODE, ConstUtil.START_ORDER); 
  48.     startAbility(intent); 

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

  1. private void initViewData() { 
  2.     LogUtils.info("initViewData"); 
  3.  
  4.     findComponentById(ResourceTable.Id_yes_btn).setClickedListener(component -> { 
  5.         sendMessage(AUTH_TYPE1); 
  6.     }); 
  7.     findComponentById(ResourceTable.Id_no_btn).setClickedListener(component -> { 
  8.         sendMessage(AUTH_TYPE2); 
  9.     }); 
  10.  
  11. private void sendMessage(int type) { 
  12.     LogUtils.info("sendMessage"); 
  13.  
  14.     //从MainAbility获取HPermission实例 
  15.     HPermission hPermission = ((MainAbility) getAbility()).getPermission(); 
  16.  
  17.     //如果用户已允许分布式权限,设置按钮可用 
  18.     hPermission.requestPermissions(this, () -> { 
  19.         // button Enabled 
  20.         findComponentById(ResourceTable.Id_yes_btn).setEnabled(false); 
  21.         findComponentById(ResourceTable.Id_no_btn).setEnabled(false); 
  22.  
  23.         //打开请求侧的页面 
  24.         startRemoteAbility(type); 
  25.     }); 
  26.  
  27. /** 
  28.  * 打开请求授权侧的MainAbility 
  29.  * 
  30.  * @param type 是否同意授权 
  31.  */ 
  32. private void startRemoteAbility(int type) { 
  33.     LogUtils.info("startRemoteAbility"); 
  34.  
  35.     DialogUtil.showToast(getContext(), type == AUTH_TYPE1 ? "允许玩游戏" : "已拒绝玩游戏"); 
  36.     Intent intent = new Intent(); 
  37.     Operation operation = 
  38.             new Intent.OperationBuilder() 
  39.                     .withDeviceId(remoteDeviceId == null ? "" : remoteDeviceId) 
  40.                     .withBundleName(getBundleName()) 
  41.                     .withAbilityName(MainAbility.class.getName()) 
  42.                     .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) 
  43.                     .build(); 
  44.     intent.setOperation(operation); 
  45.  
  46.     //授权码 
  47.     intent.setParam(ORDER_CODE, type); 
  48.     startAbility(intent); 
  49.  
  50.     //关闭当前宿主 Ability 
  51.     getUITaskDispatcher().delayDispatch(() -> terminateAbility(), DELAY); 

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

config.json

  1.   ... 
  2.   "orientation""unspecified"
  3.   "visible"true
  4.   "name""com.buty.javadistributedemo.MainAbility"
  5.   "icon""$media:icon"
  6.   "description""$string:mainability_description"
  7.   "label""$string:entry_MainAbility"
  8.   "type""page"
  9.   "launchType""singleton" 

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

  1. /** 
  2.  * Ability设置为singleton模式 
  3.  * 当创建时,如果实例已存在,触发该函数 
  4.  * 
  5.  * @param intent 
  6.  */ 
  7. @Override 
  8. protected void onNewIntent(Intent intent) { 
  9.     LogUtils.info("onNewIntent"); 
  10.     super.onNewIntent(intent); 
  11.     //是否允许 
  12.     int code = intent.getIntParam(ConstUtil.ORDER_CODE, 0); 
  13.     // 
  14.     String deviceId = intent.getStringParam(ConstUtil.DEVICE_ID); 
  15.     sendCommonEvent(code, deviceId); 

 MainAbilitySlice收到消息

  1. /** 
  2.  * 实现 RegisterManager的 CommonEvent接口 
  3.  * 
  4.  * @param code     code 
  5.  * @param deviceId deviceId 
  6.  */ 
  7. @Override 
  8. public void onReceiveEvent(int code, String deviceId) { 
  9.     LogUtils.info("onReceiveEvent,code:"+code); 
  10.     switch (code) { 
  11.         // Agree to allow games to be played 
  12.         case ConstUtil.AUTH_TYPE1: 
  13.             //start.setVisibility(Component.HIDE); 
  14.             tips.setVisibility(Component.VISIBLE); 
  15.             tips.setText("已授权,可以开始游戏"); 
  16.  
  17.             break; 
  18.         // Refuse to play games 
  19.         case ConstUtil.AUTH_TYPE2: 
  20.             tips.setVisibility(Component.VISIBLE); 
  21.             //DialogUtil.exitDialog(getAbility()); 
  22.             tips.setText("已拒绝,不可以游戏"); 
  23.             break; 
  24.         default
  25.             break; 
  26.     } 

6.思考总结

分布式中常用的通信方式:

1.Intent 直接传递参数(intent.setParam(ORDER_CODE, type))

2.公共事件订阅/发布的方式(Intent封装到CommonEventData)

3.自定义接口的方式(RegisterManager.CommonEvent)

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

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

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

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

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2021-12-02 10:11:44

鸿蒙HarmonyOS应用

2021-08-24 15:13:06

鸿蒙HarmonyOS应用

2021-09-24 09:25:01

鸿蒙HarmonyOS应用

2019-11-20 15:34:16

区块链金融去中心化

2021-11-02 10:10:49

鸿蒙HarmonyOS应用

2021-10-21 10:03:09

鸿蒙HarmonyOS应用

2021-11-16 09:38:10

鸿蒙HarmonyOS应用

2021-07-23 08:57:32

鸿蒙HarmonyOS应用

2021-07-22 10:20:21

鸿蒙HarmonyOS应用

2020-09-29 19:20:05

鸿蒙

2021-08-31 22:52:40

区块链互联网技术

2020-11-06 12:12:35

HarmonyOS

2018-07-17 08:14:22

分布式分布式锁方位

2021-08-27 09:57:18

鸿蒙HarmonyOS应用

2021-08-17 10:20:14

鸿蒙HarmonyOS应用

2021-09-15 14:55:49

鸿蒙HarmonyOS应用

2021-05-28 09:52:00

鸿蒙HarmonyOS应用

2021-12-13 11:07:10

鸿蒙HarmonyOS应用

2021-09-17 14:43:54

鸿蒙HarmonyOS应用

2019-02-13 13:41:07

MemCache分布式HashMap
点赞
收藏

51CTO技术栈公众号