场景简介

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

前置步骤

  1. 在实际集成之前,我们要确保已经进行过开发准备
  2. 在控制台创建测试用户,并配置好用户名密码认证,参考产品手册

流程图

1. 子应用启动流程

流程图

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

流程图

集成步骤

1、初始化SDK

initSDK接口详情

注意:SDK接口都需要在SDK初始化后才能调用,否则会抛出异常。

初始化SDK主要是完成SDK核心功能配置的初始化,放到Application的attachBaseContext中初始化。

示例代码如下:

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    initSDK(base);
}

/**
 * 初始化SangforSDK
 *
 * @param context
 */
private void initSDK(Context context) {
    Map<SFSDKExtras, String> extra = new HashMap<>();

    //这里需要指定为子应用的flag
    int sdkFlags = SFSDKFlags.FLAGS_SUB_APPLICATION;
    sdkFlags |= SFSDKFlags.FLAGS_VPN_MODE_TCP;

    SFUemSDK.getInstance().initSDK(context, SFSDKMode.MODE_VPN_SANDBOX, sdkFlags, extra);
}

注意

SFSDKMode.MODE_VPN_SANDBOX: 该模式是启用vpn代理功能,和启用终端安全沙箱功能(水印,分享隔离,剪切板隔离等等安全功能); 只有当集成SDK的APP在应用中心授权给用户, 用户才可以使用集成SDK的APP接入使用, 并且占用并发授权和UEM移动版授权;

SFSDKMode.MODE_VPN: 该模式表示只是启用vpn代理功能,不启用终端安全沙箱功能(水印,分享隔离,剪切板隔等等安全功能); 无论集成SDK的APP是否在应用中心授权给用户, 用户都可以使用集成SDK的APP接入使用, 并且仅占用并发授权;

沙箱文件加密功能说明:

沙箱功能中的文件加密功能是指,如果终端(手机)开启文件加密,app中需要落盘的数据会自动被加密然后重定向,以保证数据在手机存储里面的安全;

集成步骤上只需要在上面初始化的时候增加flag:SFSDKFlags.FLAGS_ENABLE_FILE_ISOLATION 并设置SDKMode为SFSDKMode.MODE_VPN_SANDBOX

示例代码如下:

/**
 * 初始化SangforSDK
 *
 * @param context
 */
private void initSDK(Context context) {
    Map<SFSDKExtras, String> extra = new HashMap<>();

    int sdkFlags = SFSDKFlags.FLAGS_HOST_APPLICATION;
    sdkFlags |= SFSDKFlags.FLAGS_VPN_MODE_TCP;
    //开启文件加密功能
    sdkFlags |= SFSDKFlags.FLAGS_ENABLE_FILE_ISOLATION;

    SFUemSDK.getInstance().initSDK(context, SFSDKMode.MODE_VPN_SANDBOX, sdkFlags, extra);
}

2、指定主应用包名

在AndroidManfiest中通过meta-data指定主应用的包名

注意:下面的包名仅为示例,集成时需要修改成对应主应用包名

   <!-- 这里需要配置主应用包名 -->
    <meta-data
        android:name="host_data"
        android:value="com.sangfor.sdkdemo" />

3、处理子应用启动场景

1) 优先尝试免密认证

子应用调用免密认证接口,如果返回成功,则不需要再拉起主应用进行登陆授权,可以直接访问资源,如果免密认证失败,再拉起主应用进行授权

示例代码如下:

if (SFUemSDK.getInstance().startAutoTicket()) {
    SFLogN.info(TAG, "startAutoTicket success");
} else {
    SFLogN.info(TAG, "ticketauth failed, will launcherHostApp");
    //需要跳转主应用登陆授权
}

2) 跳转主应用获取登录凭据

launchHostApp接口详情

免密认证失败后,需要拉起主应用获取登录凭据

注意:此处跳转前可以判断一下主应用是否已经安装,如果未安装可以提示用户安装主应用,可以参考SubAppDemo的处理

示例代码如下:

  //hostAppPkgName为主应用包名
    SFUemSDK.getInstance().getSFLaunch().launchHostApp(context, hostAppPkgName, SFLaunchReason.Launch_HOSTAPP_AUTH_AUTHORIZATION, null);

3) 子应用被主应用拉起后验证认证状态

子应用被主应用拉起后,子应用会回调onAppLaunched监听事件(初始化sdk后,需要添加监听事件,SubAppDemo的GlobalListenerManager类实现),通过SFLaunchInfo的mIsSyncDataComplete和mAuthOk决定子应用是否能够访问资源。

便于较好的用户体验,客户app可以显示一个Loading页面(类似SubAppDemo的LoginAuthActivity)来等待sdk数据共享完成后在进行资源访问。

示例代码如下:


// 子应用认证界面需要添加SyncDataListener监听
public class LoginAuthActivity extends BaseActivity implements GlobalListenerManager.SyncDataListener {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView();

        ......
        GlobalListenerManager.getInstance().registerSyncDataListener(this);
    }

    @Override
    public void onSyncDataComplete(boolean isAuthOk) {
        SFLogN.info(TAG, "onSyncDataComplete called");
        if (isAuthOk) {
            // 可以开始访问资源
        } else {
            // 需要再次拉起主应用
        }
    }
}

// 主应用拉起子应用监听回调
SFUemSDK.getInstance().getSFLaunch().setAppLaunchListener(new SFLaunchEntry.SFAppLaunchListener() {
    @Override
    public void onAppLaunched(SFLaunchInfo launchInfo) {

        if (launchInfo.isSyncDataComplete()) {
            // 完成主应用同步数据到子应用, 当前app如果在等待数据同步完成,则需要通知到ui;
            if (shouldWaitSyncData()) {
                setShouldWaitSyncData(false);
                notifySyncDataComplete(launchInfo.isAuthOk());
            }
            return;
        }


        if (launchInfo.isAuthOk()) {
            // 子应用被拉起,如果当前认证成功,则不需等待数据同步完成,即可访问资源
            setShouldWaitSyncData(false);
            return;
        }

        // 子应用被拉起,如果当前没有认证成功,则会在LoginAuthActivity显示loading状态,等待主应用sync data 到子应用这边
        setShouldWaitSyncData(true);
    }
});

4、监听注销事件

子应用使用过程中有可能出现被注销的情况,如主应用主动调用注销接口、服务端管理员主动让用户下线等,所以子应用监听注销回调是必须的,避免出现sdk内部已经注销了,但是应用无感知,从而出现业务异常的问题;

注意:注销回调建议在应用全生命周期内进行监听,避免出现在某个页面监听,页面销毁后,注销事件丢失的问题,可以参考SubAppDemo的处理方法:在sdk初始化后就用一个单例对象注册注销回调

示例代码如下:

/**
* 注销事件监听回调,推荐在Application里面监听, 避免出现认证开始了还未监听的问题
*/
SFUemSDK.getInstance().registerLogoutListener(new SFLogoutListener() {
    @Override
    public void onLogout(SFLogoutType type, SFBaseMessage message) {
        SFLogN.info(TAG, "onLogout, message: " + message);
        /**
        * 收到注销事件,应用在前台可以直接拉起主应用进行授权
        * 参考SubappDemo的处理
        */
    }
});

5、监听应用进入前台事件

子应用应该在每次进入前台都需要判断自身的认证状态是否为认证成功状态,如果不是认证成功就应该拉起认证界面(LoginAuthActivity),然后拉起主应用获取登录凭据,避免出现如下场景问题: 子应用收到注销后拉起主应用获取登录凭据,但是此时主应用还未认证,无法提供凭据,等主认证认证成功后,用户再次从桌面打开子应用,此时子应用由于没有对认证状态做检查就不会拉起主应用获取登录凭据。 具体实现参考SubAppDemo中的AppForegroundManager进行处理

示例代码如下:

// 进入前台回调
private void dispatchBecameForeground() {
    SFLogN.info(TAG, "dispatchBecameForeground...");
    /**
    * mNeedCheckAuthStatus标记用于处理应用首次启动免密认证返回成功,此时是不需要在进入前台时进行状态检查的
    * 因为底层免密逻辑有可能还未完成,此时检测状态可能还不是AuthStatusAuthOk,mNeedCheckAuthStatus这个
    * 标记的处理参考SubAppDemo
    */
    if (mNeedCheckAuthStatus) {
        try {
            checkToLauncherAuthActivity();
        }catch (Exception e) {
            SFLogN.error2(TAG, "launchHostApp failed: ", e.getMessage(), e);
        }
    }
}

private void checkToLauncherAuthActivity() {
    SFLogN.info(TAG, "checkToLauncherAuthActivity call.");
    if (SFUemSDK.getInstance().getAuthStatus() != SFAuthStatus.AuthStatusAuthOk) {
        SFLogN.info(TAG, "current is not auth success, will launcher AuthActivity.");
        // 后台回到前台,当前应用未认证,则需要进行认证
        if (mCurrentActivity != null) {
            if (mCurrentActivity instanceof LoginAuthActivity) {
                SFLogN.info(TAG, "not auth ok, but current is auth activity, can not relauncher.");
            } else {
                SFLogN.info(TAG, "not auth ok, will launcherAuthActivity for becameForeground, current is " + mCurrentActivity);
                LoginAuthActivity.startActivity(mCurrentActivity);
            }
        } else {
            SFLogN.error2(TAG, "should start AuthActivity, but can not do.", "current top activity is null");
        }
    }
}

results matching ""

    No results matching ""