Android实现外部App互信跳转SDK

版权声明:本博客主要记录学习笔记和遇到的一些问题解决方案,转载请注明出处! https://blog.csdn.net/u010982507/article/details/81144478
  • 需求描述
    最近项目上有个需求,要实现外部App跳转到自身App,并在自己App中实现免密登录功能。功能不算难,做个笔记,方便以后使用。

  • 功能分析
    1、FirstApp提供SDK的jar包,SecondApp负责调用jar包中的toActivity方法
    2、在toActivity方法中实现FirstApp提供的后台服务,验证SecondApp的合法性
    3、接口请求成功后,实现SecondApp到FirstApp之间的跳转功能,并传值
    4、FirstApp接收到SecondApp传来的值,进行登录校验

  • 代码实现
    1、在SecondApp中引入AppBridge的jar包,并调用AppBridge中的toActivity方法

String env = EnvUtil.UAT; // 测试环境
AppBridge appBridge = new AppBridge(env);
appBridge.toActivity(MainActivity.this, "参数1","参数2", new BridgeCallback() {
      @Override
      public boolean success(String result) {
          tv_response.setText(result);
          return super.success(result);
      }

      @Override
      public void fail(int code, String message) {
          tv_response.setText(message);
      }
 });

2、进入AppBridge核心实现类,在此类中会请求一个服务接口,这个接口的作用是验证SecondApp的合法性,首先要提前将SecondApp的应用名、包名和证书的sha1值存储到FirstApp服务的数据库中,然后请求接口的时候将Second客户端的包名和证书的sha1值传到FirstApp的服务器验证,这里如果不是使用https请求,参数需要加密处理。

public class AppBridge {
    private static final String TAG = AppBridge.class.getSimpleName();
    private String env;

    // 构造函数,将测试环境初始化
    public AppBridge(String env) {
        this.env = env;
    }

     /**
     * 跳转到app,并传入相关参数
     *
     * @param context    上下文
     * @param userName   用户名
     * @param role       用户角色
     * @param callback
     */
    public void toActivity(final Context context, final String userName, final String role, final  BridgeCallback callback) {
        if (callback == null) {
            return;
        }
        // 验证要跳转的app是否已经安装
        if (!EnvUtil.isAPKExists(context, EnvUtil.getPackageName(env))) {
            callback.fail(BridgeResponse.APP_NOT_EXISTS, "应用不存在.");
            return;
        }
        // 验证SecondApp的证书是否能正常读取
        String sign = EnvUtil.getSign(context);
        if (sign == null) {
            callback.fail(BridgeResponse.APP_SIGN_ERROR, "证书异常.");
            return;
        }
        String params = "{\"InArgs\":{},\"XM\":{\"Sign\":\"" + sign
                + "\",\"PackageName\":\"" + context.getPackageName()
                + "\",\"userName\":\"" + userName
                + "\",\"role\":\"" + role
                + "\"}}";
        // 调用OkHttp的post请求
        OkhttpUtil.post(EnvUtil.getUrl(this.env), params, new StringCallback() {
            @Override
            public void onSuccess(String response) {
                super.onSuccess(response);
                try {
                    if (!TextUtils.isEmpty(response)) {
                        JSONObject result = new JSONObject(response);
                        JSONObject OutArgs = result.getJSONObject("OutArgs");
                        String errorCode = OutArgs.getString("errorcode");
                        if (BridgeResponse.RES_SUCCESS_CODE.equals(errorCode)) {
                            // 服务请求成功
                            AppInfo appInfo = new AppInfo();
                            appInfo.getJSONObject(result);
                            // 实现app之间的跳转
                            Intent intent = new Intent();
                            intent.setComponent(new ComponentName(EnvUtil.getPackageName(env), EnvUtil.Entry));
                            // 拿到唯一的token,作为FirstApp登录使用
                            intent.putExtra("Token", appInfo.getToken());
                            intent.putExtra("userName", appInfo.getUserName());
                            intent.putExtra("Role", appInfo.getRole());
                            context.startActivity(intent);
                            callback.success(response);
                        } else {
                            callback.fail(Integer.parseInt(errorCode), OutArgs.getString("msg"));
                        }
                    } else {
                        callback.fail(BridgeResponse.PARAMS_ERROR, "服务器返回值异常." + response);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    callback.fail(BridgeResponse.APP_NOT_EXISTS, e.getMessage());
                }
            }

            @Override
            public void onError(String result, int code) {
                super.onError(result, code);
                callback.fail(code, result.toString());
            }

            @Override
            public void onFailure(Request request, IOException e) {
                super.onFailure(request, e);
                callback.fail(BridgeResponse.PARAMS_ERROR, e.getMessage());
            }
        });
    }

}

3、EnvUtil环境配置工具类

public class EnvUtil {

    public static final String DEV = "dev"; // 开发环境
    public static final String TEST = "test"; // 本地测试环境
    public static final String UAT = "uat"; // 测试环境
    public static final String PRO = "product"; // 生产环境
    public static final String URL_PATH = "/servlets/AppService/user/...";
    public static final String Entry = "com.appbridge.first.app.MainActivity";

    /**
     * 获取当前app的签名信息
     *
     * @param context
     * @return
     */
    public static String getSign(Context context) {
        try {
            PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
            Signature[] signs = packageInfo.signatures;
            Signature sign = signs[0];
            MessageDigest md = MessageDigest.getInstance("SHA1");
            md.update(sign.toByteArray());
            char[] hexDigits = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
            byte[] bs = md.digest();
            int j = bs.length;
            char[] buf = new char[j * 3 - 1];
            int k = 0;
            for (int i = 0; i < j; ++i) {
                byte byte0 = bs[i];
                buf[k++] = hexDigits[byte0 >>> 4 & 15];
                buf[k++] = hexDigits[byte0 & 15];
                if (i != j - 1) {
                    buf[k++] = 58;
                }
            }
            return new String(buf);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取包名
     *
     * @param env
     * @return
     */
    public static String getPackageName(String env) {
        String packageName = "com.appbridge.first";
        switch (env) {
            case DEV:
                packageName = "com.appbridge.first.dev";
                break;
            case TEST:
                packageName = "com.appbridge.first.devs";
                break;
            case UAT:
                packageName = "com.appbridge.first.uat";
                break;
            case PRO:
                packageName = "com.appbridge.first";
                break;
        }
        return packageName;
    }

    /**
     * 获取环境的url请求地址
     *
     * @param env
     * @return
     */
    public static String getUrl(String env) {
        String url = "";
        switch (env) {
            case DEV:
                url = "https://127.0.0.1:8080" + URL_PATH;
                break;
            case TEST:
                url = "https://127.0.0.1:8080" + URL_PATH;
                break;
            case UAT:
                url = "https://127.0.0.1:8080" + URL_PATH;
                break;
            case REL:
                url = "https://127.0.0.1:8080" + URL_PATH;
                break;
            case PRO:
                url = "https://127.0.0.1:8080" + URL_PATH;
                break;
        }
        return url;
    }

    /**
     * 判断是否安装某个应用
     *
     * @param context
     * @param packageName
     * @return
     * @throws Exception
     */
    public static boolean isAPKExists(Context context, String packageName) {
        boolean isExists = false;
        try {
            PackageManager packageManager = context.getPackageManager();
            List<PackageInfo> info = packageManager.getInstalledPackages(0);// 获取所有已安装程序的包信息
            if (info != null) {
                for (int i = 0; i < info.size(); i++) {
                    String pn = info.get(i).packageName;
                    if (packageName.equals(pn)) {
                        isExists = true;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            isExists = false;
        }
        return isExists;
    }
}

4、AppInfo实体类,这个类只是映射后台json报文

public class AppInfo {
    private String Token;
    private String UserName;
    private String Role;

    public void getJSONObject(JSONObject response) {
        try {
            JSONObject result = response.getJSONObject("result");
            JSONObject xm = result.getJSONObject("xm");
            this.Token = xm.getString("Token");
            this.UserName= xm.getString("UserName");
            this.Role = xm.getString("Role");
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    public String getToken() {
        return Token;
    }

    public void setToken(String token) {
        Token = token;
    }

    public String getUserName() {
        return UserName;
    }

    public void setUserName(String UserName) {
        UserName= UserName;
    }

    public String getRole() {
        return Role;
    }

    public void setRole(String role) {
        Role = role;
    }

}

5、toActivity的回调接口类

public abstract class BridgeCallback {

    public boolean success(String uuid) {
        return true;
    }

    public abstract void fail(int code, String message);
}

6、错误码处理

public class BridgeResponse {

    public static final String RES_SUCCESS_CODE = "0000";
    public static final int APP_NOT_EXISTS = 1001;
    public static final int APP_SIGN_ERROR = 1002;
    public static final int PARAMS_ERROR = 1003;
}

备注:代码中有个OkHttpUtil类代码没有贴出,这个使用自己项目中的工具类即可。

猜你喜欢

转载自blog.csdn.net/u010982507/article/details/81144478