场景简介
当同时存在多个app需要集成sdk,且多个app需要使用同一个用户登陆时,如果每个应用都去做用户名密码认证,不仅体验不佳,且会占用多个授权,为了解决此场景,sdk提供了专门用于主从场景的接口,只需要主应用做用户名密码认证,子应用通过接口向主应用同步授权信息即可进行免密上线
前置步骤
流程图
1. 子应用启动流程

2. 子应用后台回到前台流程

集成步骤
1、初始化SDK
注意:SDK接口都需要在SDK初始化后才能调用,否则会抛出异常。
初始化SDK主要是完成SDK核心功能配置的初始化,放到AbilityStage的onCreate中初始化。
示例代码如下:
onCreate(): void {
this.init()
}
init() {
let extra: Map<SFSDKExtras, string> = new Map<SFSDKExtras, string>();
let sdkFlags: number = 0;
sdkFlags = SFSDKFlags.FLAGS_SUB_APPLICATION; // 设置子应用标识
sdkFlags |= SFSDKFlags.FLAGS_VPN_MODE_TCP;
sdkFlags |= SFSDKFlags.FLAGS_SDP_SERVER; // 表明对接的是SDP
SFUemSDK.getInstance().initSDK(
this.context,
SFSDKMode.MODE_SUPPORT_MUTABLE,
sdkFlags, extra
);
// 注册拉起回调接口,当被子应用拉起时,会回调onAppLaunched函数
SFUemSDK.getInstance().getSFLaunch().setLaunchListener(this)
// 注册注销事件监听回调,注销事件中可以拉起主应用重新授权
SFUemSDK.getInstance().registerLogoutListener(this);
}
注意
SFSDKFlags.FLAGS_SUB_APPLICATION 该模式是启用vpn代理功能,和启用终端安全沙箱功能(水印,分享隔离,剪切板隔离等等安全功能); 只有当集成SDK的APP在应用中心授权给用户, 用户才可以使用集成SDK的APP接入使用, 并且占用并发授权和UEM移动版授权;
SFSDKMode.MODE_SUPPORT_MUTABLE: SFSDKMode.MODE_SUPPORT_MUTABLE
沙箱文件加密功能说明:
沙箱功能中的文件加密功能是指,如果终端(手机)开启文件加密,app中需要落盘的数据会自动被加密然后重定向,以保证数据在手机存储里面的安全;
2、指定主应用包名
在module.json5中通过metadata指定主应用的包名
注意:下面的包名仅为示例,集成时需要修改成对应主应用包名
<!-- 这里需要配置主应用包名 -->
"metadata": [
{
"name": "main_auth_app_bundleName", // 自定义键名, 存储认证app的bundleName的name值
"value": "com.sangfor.sdkdemo", // 自定义值(字符串)存储认证app的bundleName值
}
]
3、处理子应用启动场景
1) 优先尝试免密认证
子应用调用免密认证接口,如果返回成功,则不需要再拉起主应用进行登陆授权,可以直接访问资源,如果免密认证失败,再拉起主应用进行授权
示例代码如下:
const isReady = SFUemSDK.getInstance().startAutoTicket()
if (!isReady) {
//需要跳转主应用登陆授权
}
2) 跳转主应用获取登录凭据
免密认证失败后,需要拉起主应用获取登录凭据
注意:此处跳转前可以判断一下主应用是否已经安装,如果未安装可以提示用户安装主应用
示例代码如下:
//customValue为主应用包名
const metadata = this.context.abilityInfo.metadata;
const customValue = metadata.find(item => item.name === "main_auth_app_bundleName")?.value;
const result = await launch.launchApp(customValue!,
SFLaunchReason.SFLaunchReasonHostappAuthAuthorization,
"extraData")
3) 子应用被主应用拉起后验证认证状态
子应用被主应用拉起后,子应用会回调onAppLaunched监听事件(初始化sdk后,需要添加监听事件,SubApp的EntryAbilityStage类实现),通过getAuthStatus返回的布尔值决定子应用是否能够访问资源。
便于较好的用户体验,客户app可以显示一个Loading页面来等待sdk数据共享完成后在进行资源访问。
示例代码如下:
// 子应用认证界面需要添加SyncDataListener监听
export default class EntryAbilityStage extends AbilityStage implements SFLaunchListener {
onCreate(): void {
this.init()
}
init() {
let extra: Map<SFSDKExtras, string> = new Map<SFSDKExtras, string>();
let sdkFlags: number = 0;
sdkFlags = SFSDKFlags.FLAGS_SUB_APPLICATION; // 设置子应用标识
sdkFlags |= SFSDKFlags.FLAGS_VPN_MODE_TCP;
sdkFlags |= SFSDKFlags.FLAGS_SDP_SERVER; // 表明对接的是SDP
SFUemSDK.getInstance().initSDK(
this.context,
SFSDKMode.MODE_SUPPORT_MUTABLE,
sdkFlags, extra
);
// 注册拉起回调接口,当被子应用拉起时,会回调onAppLaunched函数
SFUemSDK.getInstance().getSFLaunch().setLaunchListener(this)
// 注册注销事件监听回调,注销事件中可以拉起主应用重新授权
SFUemSDK.getInstance().registerLogoutListener(this);
}
/**
* 应用被拉起事件
* @param launchInfo
*/
onAppLaunched(launchInfo: SFLaunchInfo): void {
this.handleBeLaunched(launchInfo)
}
private async handleBeLaunched(launchInfo: SFLaunchInfo) {
// 接收用户自定义数据
console.log(`launchInfo.reason ${launchInfo.launchReason} extra: ${launchInfo.extraData}`);
const authStatus = await SFUemSDK.getInstance().getSFAuth().getAuthStatus()
// 可以进入业务界面并开始访问资源。
if (authStatus) {
console.log("auth ok");
}
return
}
4、监听注销事件
子应用使用过程中有可能出现被注销的情况,如主应用主动调用注销接口、服务端管理员主动让用户下线等,所以子应用监听注销回调是必须的,避免出现sdk内部已经注销了,但是应用无感知,从而出现业务异常的问题;
注意:注销回调建议在应用全生命周期内进行监听,避免出现在某个页面监听,页面销毁后,注销事件丢失的问题,可以参考SubApp的处理方法:在sdk初始化后就用一个单例对象注册注销回调
示例代码如下:
/**
* 注销事件监听回调,推荐在Application里面监听, 避免出现认证开始了还未监听的问题
*/
/**
* 注销结果回调
* @param type 注销类型
* @param message 注销信息
*/
onLogout(type: SFLogoutType, message: SFBaseMessage) {
}
5、监听应用进入前台事件
子应用应该在每次进入前台都需要判断自身的认证状态是否为认证成功状态,如果不是认证成功就应该拉起认证界面,然后拉起主应用获取登录凭据,避免出现如下场景问题: 子应用收到注销后拉起主应用获取登录凭据,但是此时主应用还未认证,无法提供凭据,等主认证认证成功后,用户再次从桌面打开子应用,此时子应用由于没有对认证状态做检查就不会拉起主应用获取登录凭据。 具体实现参考SubApp中的 onForeground(): void {进行处理
示例代码如下:
// 进入前台回调
onForeground(): void {
// Ability has brought to foreground
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
this.checkLogin();
}
checkLogin(): void {
// 授权弹框需要windowStage创建完毕
if (!this.isWindowStageReady) {
this.isCheckLoginBlock = true
return
}
const loginObj = AppStorageV2.connect(LoginProfile, "loginProfile", ()=> new LoginProfile())
const isReady = SFUemSDK.getInstance().startAutoTicket()
if (loginObj) {
loginObj.isLogin = isReady;
}
if (!isReady) {
this.launchMainApp()
}
}
private async launchMainApp() {
const launch = SFUemSDK.getInstance().getSFLaunch()
const metadata = this.context.abilityInfo.metadata;
const customValue = metadata.find(item => item.name === "main_auth_app_bundleName")?.value;
const result = await launch.launchApp(customValue!,
SFLaunchReason.SFLaunchReasonHostappAuthAuthorization,
"extraData")
}