阿里云首页 安全认证 相关技术圈

Android SDK 接入

本文是Android客户端SDK 集成说明文档。

1. 概述

官网下载SDK后进行解压,解压后包含:

2. 前期准备

2.1 准备工作

如果应用开启了手机号认证服务, 请确保终端设备已经开启4G网络(联通、移动支持3G网络, 但接口耗时会增加),手机号认证授权之后,联通电信可立即使用, 移动需等待10分钟后使用。

2.2 运行demo工程

解压Android Demo工程, 需要将包名、applicationId、应用密钥、服务密钥(手机号认证)修改为正确的值。

2.3 搭建开发环境

应用必须运行在Android 4.1+平台上。

2.3.1 导入aar

1

2.3.2 使用aar

12

2.3.3 混淆忽略

若开启资源混淆,需要配置

"R.drawable.authsdk*",
"R.layout.authsdk*",
"R.anim.authsdk*",
"R.id.authsdk*",
"R.string.authsdk*",
"R.style.authsdk*",

-keep class com.nirvana.** { *; }
-keep class com.uc.crashsdk.** { *; }
-keep class com.idsmanager.doraemon.** { *; }

2.3.4 权限添加

<uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 检查wifi网络状态 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 检查网络状态 -->
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <!-- 切换网络通道 -->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- 开关wifi状态,解决国内机型移动网络权限问题需要 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- 本地信息缓存 -->
    <!-- 可选权限 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" /

2.3.5 Activity 声明

<!--联通电信授权页-->  
<activity
    android:name="com.mobile.auth.gatewayauth.LoginAuthActivity"  
    android:configChanges="orientation|keyboardHidden|screenSize"  
    android:exported="false"
    android:theme="@style/authsdk_activity_dialog"          使用弹窗模式必须添加!!!
    android:launchMode="singleTop" />  
<!--协议页面webview-->  
<activity  
    android:name="com.mobile.auth.gatewayauth.activity.AuthWebVeiwActivity"  
    android:configChanges="orientation|keyboardHidden|screenSize"  
    android:exported="false"  
    android:launchMode="singleTop" 
    android:screenOrientation="behind" />  
 <!--移动授权页-->  
 <activity  
    android:name="com.cmic.sso.sdk.activity.LoginAuthActivity"  
    android:configChanges="orientation|keyboardHidden|screenSize"
    android:exported="false"  
    android:launchMode="singleTop" />

2.3.6 Support 包

使用v4包com.android.support:support-v4版本高于25.4.0,或者v7包com.android.support:appcompat-v7版本高于25.4.0.

2.3.7 获取app md5 签名值

使用Android studio gradle,点击signingReport,得到MD5签名值,将冒号去掉并转小写。

说明

debug包和release包签名是不一样的,避免用debug的签名值用于生产测试。

111

3. SDK 调用说明

需要在APP中集成安全认证客户端SDK,并在服务端完成API对接。应用密钥和服务密钥获取请查看应用管理。

3.1 本机号码登录

12

  • 调用代码IDaaSDoraemonManager.getInstance获取安全认证SDK单例对象doraemonManager;

  • 调用doraemonManager的setDoraemonSDKInfo设置初始化应用秘钥参数;

  • 初始化手机号认证获取token实例mTokenListener =new TokenResultListener() {};

  • 初始化手机号认证SDK实例mPhoneNumberAuthHelper=PhoneNumberAuthHelper.getInstance(context, mToke nListener);

  • 设置手机号认证秘钥mPhoneNumberAuthHelper.setAuthSDKInfo(AUTH_SECRET);

  • 调用代码检测设备是否支持续本机号码登录mPhoneNumberAuthHelper.checkEnvAvailable(PhoneNumberAuthHelper.SERVICE_TYPE_LOGIN);

  • 调用本机号码登录方法mPhoneNumberAuthHelper.getLoginToken(this,timeout);

  • 获取到token后,调用doraemonManager.getMobileExtendParamsJson(token,null,newMobileExtendParamsService());获取最终请求invoke认证接口参数。

3.2 本机号码校验

123

  • 调用代码IDaaSDoraemonManager.getInstance获取安全认证SDK单例对象doraemonManager;

  • 调用doraemonManager的setDoraemonSDKInfo设置初始化应用秘钥参数;

  • 初始化手机号认证获取token实例mVerifyListener=newTokenResultListener() {};

  • 初始化手机号认证SDK实例mPhoneNumberAuthHelper= PhoneNumberAuthHelper.getInstance(context, mToke nListener);

  • 设置手机号认证秘钥mPhoneNumberAuthHelper.setAuthSDKInfo(AUTH_SECRET);

  • 调用代码检测设备是否支持本机号码校验mPhoneNumberAuthHelper.checkEnvAvailable(PhoneNumberAuthHelper.SERVICE_TYPE_LOGIN);

  • 调用手机号认证方法mPhoneNumberAuthHelper.setAuthListener(mVerifyListener); mPhoneNumberAuthHelper.getVerifyToken(timeout);

  • 获取到token后,调用doraemonManager.getMobileExtendParamsJson(token,null,newMobileExtendParamsService());获取最终请求invoke认证接口参数。

4. 手机号认证SDK方法说明

4.1 主类IDaaSDoraemonManager介绍

4.1.1 获取认证实例

/**
     * 获取认证实例
     *
     * @return 获取该类的单例实例对象
     */
    public static IDaaSDoraemonManager getInstance(Context context);

4.1.2 获取sdk版本号(getVersion)

/**
     * 获取sdk版本号
     *
     * @return 字符串,sdk版本号
     */
    public String getVersion();

4.1.3 设置阿里云安全认证应用秘钥(setDoraemonSDKInfo)

/**
     * 初始化SDK调用参数,app生命周期内调用一次
     *
     * @param base64EncodeKey app对应的秘钥
     */
    public void setDoraemonSDKInfo(String base64EncodeKey);

4.1.4 获取最终请求invoke认证接口参数

/**
     * 获取最终请求invoke认证接口参数
     *
     * @param accessToken 调用不同的服务获取到不同认证的信息(手机号认证、IFAA),例如手机号认证是accees_token, IFAA是Json字符串
     * @param phoneNumber 手机号码(只有本机号码校验才需要必传)
     * @param service     结果回调到主线程
     */
    public void getMobileExtendParamsJson(String accessToken, String phoneNumber, MobileExtendParamsService service);

4.2 主类PhoneNumberAuthHelper介绍

4.2.1 获取认证实例(getInstance)

/**
 * 获取手机号认证服务示例,此实例为单例,获取多次为同一对象
 * @param context       Android上下文
 * @param tokenListener 需要实现的获取token回调
 * @return PhoneNumberAuthHelper
 */  
public static PhoneNumberAuthHelper getInstance(Context context,  TokenResultListener tokenListener)

4.2.2 检查认证环境(checkAuthEnvEnable)

/**
 * SDK环境检查函数,检查终端是否⽀持手机号认证,通过TokenResultListener返回code
 * type 1:本机号码校验 2: ⼀键登录
 * 600024 终端⽀持认证
 * 600013 系统维护,功能不可⽤
 */
 public void checkEnvAvailable(@IntRange(from = 1, to = 2) int type)

4.2.3 加速本机号码校验(accelerateVerify)

/**  
 public void accelerateVerify(int overdueTime, final PreLoginResultListener listener);

4.2.4 本机号码校验token(getVerifyToken)

/**
* 获取认证token
*
* @param totalTimeout 超时时间 单位ms
*/
 public void getVerifyToken(final int totalTimeout)

4.2.5 加速授权页拉起(accelerateLoginPage)

/**
* 加速授权⻚唤起
*
* @param overdueTime 预取号有效期
* @param listener    预取号回调
*/
public void accelerateLoginPage(final int overdueTime, final PreLogin ResultListener listener)

4.2.6 本机号码登录唤起授权页(getLoginToken)

/**
* 获取登录token 调起本机号码登录授权⻚⾯,在⽤户授权后获取本机号码登录的Token
*
* @param totalTimeout 超时时间 单位ms
*/
public void getLoginToken(final Context context, final int totalTimeout)

4.2.7 退出授权页(quitLoginPage)

/**
* 退出授权认证⻚
* sdk完成回调之后不会关闭授权⻚,需要开发者主动调⽤quitLoginPage退出授权⻚
*/
public void quitLoginPage()

4.2.8 关闭授权页loading(hideLoginLoading)

/**
* 关闭授权⻚loading
* sdk完成回调之后不会关闭loading,需要开发者主动调⽤hideLoginLoading关闭loading
*/
public void hideLoginLoading()

4.2.9 返回默认上⽹卡运营商(getCurrentCarrierName)

/**
* 返回默认上⽹卡运营商
*
* @return CMCC、CUCC、CTCC
*/
public String getCurrentCarrierName()

4.2.10 使用xml添加自定义控件至本机号码登录授权页 (addAuthRegisterXmlConfig)

/**
* 添加⾃定义View
*
* @param xmlConfig
*/
public void addAuthRegisterXmlConfig(AuthRegisterXmlConfig xmlConfig)

⼀次add,XML内绘制的⾃定义控件全部添加完成。

初始化 addAuthRegisterXmlConfig 类时需要先调静态内部类Builder()⾥⾯的 2 个⽅法。

  • setLayout: 开发者传⼊⾃定义的控件的xml资源ID。

  • AbstractPnsViewDelegate:授权⻚使⽤xml添加⾃定义布局时,可以配合该Delegate类实现xml中相 关view的操作,⽐如事件监听以及动态UI改动等等,当xml对应的view加载后SDK将调⽤ onViewCreated(View)⽅法通知view已经创建OK,此时可以获取xml中的view并进⾏相关事件绑定等 操作。

说明

onViewCreated(View)中返回的View不能用强引用,或者使用完需及时释放,否则容易造成内存泄漏。

调用示例

mAlicomAuthHelper.addAuthRegisterXmlConfig(new AuthRegisterXmlConfig .Builder()
 .setLayout(R.layout.xxxxxx, new AbstractPnsViewDelegate() {
    @Override public void onViewCreated(View view) {
        //这⾥返回的View,不建议⽤强引⽤,或者使用完及时释放,否则容易造成内存泄漏
        findViewById(R.id.xxxx).setOnClickListener(new View.OnClickL istener() {
            @Override public void onClick(View v) {
                //do something
            }
        });
    }
})
.build());

4.2.11 添加代码编写的自定义控件至登录授权页(addAuthRegistViewConfig)

/**
* 动态添加控件
*
* @param viewID 开发者⾃定义控件名称
* @param viewConfig 配置开发者⾃定义控件的控件来源、位置和处理逻辑
*/
public void addAuthRegistViewConfig(String viewID, AuthRegisterViewConfig viewConfig)

说明

每次调用 getVerifyToken 授权请求之前,都需初始化⼀次 AuthRegisterViewConfig,因为在授权页关闭时都会清空注⼊进去的 AuthRegisterViewConfig, 具体实现请见demo⼯程。

初始化 AuthRegisterViewConfig 类时需要先调静态内部类Builder()⾥⾯的 3 个⽅法。

  • setView: 开发者传⼊⾃定义的控件,开发者需要提前设置好控件的布局属性,SDK 只⽀持 RelativeLayout 布局。

  • setRootViewId:设置控件的位置,⽬前SDK 授权⻚允许在3个位置插⼊开发者控件。RootViewId.ROOT_VIEW_ID_TITLE_BAR,标题栏。RootViewId.ROOT_VIEW_ID_BODY,授权⻚空⽩处。RootViewId.ROOT_VIEW_ID_NUMBER,授权⻚号码掩码区域。

  • setCustomInterface:设置控件事件。public Builder setCustomInterface(CustomInterface customInterface)。

调⽤示例:

mAlicomAuthHelper.addAuthRegistViewConfig("switch_acc_tv", new AuthRe gisterViewConfig.Builder()
.setView(mRL)
.setRootViewId(AuthRegisterViewConfig.RootViewId.ROOT_VIEW_ID_BODY)
.setCustomInterface(new CustomInterface() {
   @Override
    public void onClick(Context context) {
    startActivityForResult(new Intent(context, SecondActivity.class), 1234);
    }
 }).build());

获取 token 成功之后,需把通过setView()注⼊进去的view 置为 null。

4.2.12 ⼀键登录修改授权页主题(setAuthUIConfig)

/**
* 修改授权⻚⾯主题,开发者可以通过 此⽅法修改授权⻚⾯主题,需在 getLoginToken接⼝ 之前调⽤
*
* @param authUIConfig 登录授权⻚UI⾃定义配配置
*/
public void setAuthUIConfig(AuthUIConfig authUIConfig)

调⽤示例:

setAuthUIConfig(new AuthUIConfig.Builder()
.setLogBtnText("本机号码登录")
.setLogBtnClickableColor(Color.BLACK)
.setLogBtnUnClickableColor(Color.BLUE)
.setLogBtnTextColor(Color.WHITE).setLogoHidden(false)
.setNavColor(0xff026ED2)
.setNavText("免密登录")
.setNavTextColor(Color.WHITE)
.setNumberColor(Color.WHITE)
.setNumberSize(28)
.setNumberColor(0xff000000).create());

5. 手机号认证SDK 回调说明

5.1 获取token 回调

  • 回调返回的ret都通过 TokenRet tokenRet = JSON.parseObject(ret, TokenRet.class) 解析;

  • 授权⻚唤起成功、获取token成功都会回调onTokenSuccess⽅法(要区分两次成功可以通过返回码来 区分);

  • 获取token失败会回调onTokenFailed.

示例代码

mTokenListener = new TokenResultListener() {
 @Override
 public void onTokenSuccess(final String ret) {
 MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    /*
                     *   setText just show the result for get token。
                     *   use ret to verfiy number。
                     */
                    //ResultCode#CODE_START_AUTHPAGE_SUCCESS是授权⻚唤起成功码,若不需要处理,则过滤
                     if (ResultCode.CODE_START_AUTHPAGE_SUCCESS.equa ls(tokenRet.getCode())) {
                           return;
                    }

                    TokenRet tokenRet = JSON.parseObject(ret, TokenR et.class);
                    if (tokenRet != null) {
                        token = tokenRet.getToken();
                    }
                    mAlicomAuthHelper.quitLoginPage();
                }
            });
        }

        @Override
        public void onTokenFailed(final String ret) {
            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    /*
                     *  setText just show the result for get token
                     *  do something when getToken failed, such as u se sms verify code.
                     */
                    TokenRet tokenRet = JSON.parseObject(ret, TokenR et.class);
                    mAlicomAuthHelper.quitLoginPage();
                }
            });
        }
    };

5.2 加速唤起授权页/加速本机号码校验回调

public interface PreLoginResultListener {
/**
* @param  vendor 返回预取成功运营商
*/
void onTokenSuccess(String vendor);
/**
* @param vendor 返回预取失败运营商
* @param ret 返回失败原因
*/
void onTokenFailed(String vendor, String ret);
}

预取号回调示例代码

mPreLoginResultListener = new PreLoginResultListener() {
            @Override
            public void onTokenSuccess(final String s) {
                MainActivity.this.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                       /*
                        *  推荐在登录⻚初始化的时候调⽤
                        *  如果没有合适的调⽤时机
                        *  不调⽤此接⼝也没关系
                        *  千万不要APP冷启动初始化就调⽤!!!!
                        *  不要调⽤完预取号后⻢上调⽤getLoginToken!!!
                        *  最好判断⽤户是否登录,若已登录不要使⽤此接 ⼝!!!!
                        */
                        mRetTV.setText("预取号成功:" + s);
                    }
                });
            }

            @Override
            public void onTokenFailed(final String s, final String s 1) {
                /*
                 *  预取号调⽤失败
                 *  不⽤太关注,还是可以直接在⽤户点击"登录"时,调⽤getLoginT oken
                 */
                mRetTV.setText("预取号失败:" + s + s1);
            }
        });
     }
}

5.3 控件点击事件回调

授权页控件点击事件通过此回调返回

public interface AuthUIControlClickListener {
/**
*
* @param code     控件点击事件code
* @param context  Android上下⽂
* @param jsonObj  点击事件返回的具体内容,不同控件返回的事件内容有所不同
*/
void onClick(String code, Context context, JSONObject jsonObj);
}

示例代码

mAlicomAuthHelper.setUIClickListener(new AuthUIControlClickListener( ) {
            @Override
            public void onClick(String code, Context context, JSONObj ect jsonObj) {
                Log.e("xxxxxx", "OnUIControlClick:code=" + code + ", jsonObj=" + (jsonObj ==     null ? "" : jsonObj.toJSONString()));
}
});

5.4 建议代码调用顺序

/*
*   1.初始化获取token实例
*/
mTokenListener = new TokenResultListener() {}

/*
*   2.初始化SDK实例
*/
mAlicomAuthHelper = PhoneNumberAuthHelper.getInstance(context, mToke nListener);
/*
*   3.设置SDK秘钥
*/
mAlicomAuthHelper.setAuthSDKInfo();
/*
*   4.检测终端⽹络环境是否⽀持手机号认证,根据回调结果确定是否可以使⽤本机号码登录功能
*/
mAlicomAuthHelper.checkEnvAvailable(PhoneNumberAuthHelper#SERVICE_TY PE_LOGIN);

/*
*   5.若步骤4返回true,则根据业务情况,调⽤预取号或者本机号码登录接⼝
*     详⻅demo接⼊⼯程
*/
mAlicomAuthHelper.getLoginToken(context, 5000);

5.5 本机号码登录获取⼿机号

当成功获取到getLoginToken成功获取token后,将token传递至服务端,服务端携带token调用阿里云的getMobile接口即可进行最终的取号操作。

5.6 本机号码校验结果

当成功获取到getVerifyToken成功获取token后,将token传递至服务端,服务端携带token调用阿里云的VerifyMobile接口即可进行最终的取号操作。

6. Demo 示例

package com.idsmanager.doraemonapplication;

import android.app.ProgressDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import com.idsmanager.doraemon.IDaaSDoraemonManager;
import com.idsmanager.doraemon.service.MobileExtendParamsService;
import com.idsmanager.doraemonapplication.config.BaseUIConfig;
import com.mobile.auth.gatewayauth.PhoneNumberAuthHelper;
import com.mobile.auth.gatewayauth.ResultCode;
import com.mobile.auth.gatewayauth.TokenResultListener;
import com.mobile.auth.gatewayauth.model.TokenRet;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    //需要替换为自己的AUTH_SECRET和APP_KEY信息
    public static final String AUTH_SECRET = "IzVsTj8yEifpKEjasDeKbz580U4diLNQT3H6aWPjTBFWWZYlKLUQaoIfdMrLv8Zg/YoSQokBZ+1O/RU95RGyKwf/YUQIhG+8jkkF6I+5IcWy9GsGqS+a/ipOZfkTCJGiw9DkMlXDGp2mDpSITCddqBB6Fbpis+7AS71JkZRl7+q5EldwSCsZeKiqhO8jD9+tERkKp8FpEjbVjKRPEl/hw7Q0PicoYdNeXTir6noRhpMA3wtquA1pv8/s9TCs0dpfgpVYL6rERtzU1dPlRkHIq4c7TYMUVttR9e3xG2MxOmCJVWwqmPGsSrMPpQyfPC0c";
    public static final String APP_KEY = "MG10MDdoQWFBZ00zNUxteg==";
    private PhoneNumberAuthHelper mPhoneNumberAuthHelper;
    private TokenResultListener tokenResultListener;
    private TokenResultListener mVerifyListener;
    private BaseUIConfig mUIConfig;
    private ProgressDialog mProgressDialog;
    private EditText etPhone;
    String phoneNumber;
    IDaaSDoraemonManager doraemonManager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        etPhone =findViewById(R.id.et_phone);
        doraemonManager = IDaaSDoraemonManager.getInstance(MainActivity.this);
        doraemonManager.setDoraemonSDKInfo(APP_KEY);
        //本机号码登录
        findViewById(R.id.btn_one_login).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        oneLogin();

                    }
                });
            }
        });
        //本机号码校验
        findViewById(R.id.btn_phone_check).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                phoneCheck();
            }
        });
    }
    //本机号码校验
    private void phoneCheck() {
        phoneNumber = etPhone.getText().toString();
        //判断手机号是否合法
        if (!TextUtils.isEmpty(phoneNumber)) {
            mVerifyListener = new TokenResultListener() {
                @Override
                public void onTokenSuccess(String s) {
                    Log.i(TAG, "获取token成功:" + s);
                    try {
                        TokenRet pTokenRet = TokenRet.fromJson(s);
                        final String token = pTokenRet.getToken();
                        hideLoadingDialog();
                        if (ResultCode.CODE_SUCCESS.equals(pTokenRet.getCode()) && !TextUtils.isEmpty(token)) {
                            doraemonManager.getMobileExtendParamsJson(token, null, new MobileExtendParamsService() {
                                @Override
                                public void invoke(final String base64EncodedMobileExtendParamsJson, final String base64EncodedMobileExtendParamsJsonSign) {
                                    Log.d(TAG, "invoke() called with: base64EncodedMobileExtendParamsJson = [" + base64EncodedMobileExtendParamsJson + "], base64EncodedMobileExtendParamsJsonSign = [" + base64EncodedMobileExtendParamsJsonSign + "]");
                                    MainActivity.this.runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            Toast.makeText(MainActivity.this, "invoke成功,base64EncodedMobileExtendParamsJson=" + base64EncodedMobileExtendParamsJson + ";base64EncodedMobileExtendParamsJsonSign=" + base64EncodedMobileExtendParamsJsonSign, Toast.LENGTH_LONG).show();
                                        }
                                    });
                                }

                                @Override
                                public void success(final boolean success) {
                                    Log.d(TAG, "success() called with: success = [" + success + "]");
                                    MainActivity.this.runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            Toast.makeText(MainActivity.this, "success,success=" + success, Toast.LENGTH_LONG).show();
                                        }
                                    });
                                }

                                @Override
                                public void message(final String error) {
                                    Log.d(TAG, "message() called with: error = [" + error + "]");
                                    MainActivity.this.runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            Toast.makeText(MainActivity.this, "message,error=" + error, Toast.LENGTH_LONG).show();
                                        }
                                    });
                                }
                            });
                            Log.d(TAG, "onTokenSuccess() called with: (tokenRet.getToken() = [" + (token + "]"));
                        }
                        mPhoneNumberAuthHelper.setAuthListener(null);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onTokenFailed(final String s) {
                    Log.i(TAG, "获取token失败:" + s);
                    MainActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this, "获取token失败:" + s, Toast.LENGTH_LONG).show();
                        }
                    });
                    hideLoadingDialog();
                    setResult(2);
                    mPhoneNumberAuthHelper.setAuthListener(null);
                }
            };
            mPhoneNumberAuthHelper = PhoneNumberAuthHelper.getInstance(getApplicationContext(), mVerifyListener);
            mPhoneNumberAuthHelper.getReporter().setLoggerEnable(true);
            mPhoneNumberAuthHelper.setAuthSDKInfo(AUTH_SECRET);
            showLoadingDialog("正在进行本机号码校验");
            numberAuth(5000);
        } else {
            Toast.makeText(MainActivity.this, "请输入手机号", Toast.LENGTH_LONG).show();
        }
    }
    //本机号码登录
    private void oneLogin() {
        tokenResultListener = new TokenResultListener() {
            @Override
            public void onTokenSuccess(String s) {
                Log.d(TAG, "onTokenSuccess() called with: s = [" + s + "]");
                TokenRet tokenRet = null;
                try {
                    tokenRet = TokenRet.fromJson(s);
                    if (ResultCode.CODE_START_AUTHPAGE_SUCCESS.equals(tokenRet.getCode())) {
                        Log.i("TAG", "唤起授权页成功:" + s);
                        hideLoadingDialog();
                    }

                    if (ResultCode.CODE_SUCCESS.equals(tokenRet.getCode())) {
                        hideLoadingDialog();
                        mPhoneNumberAuthHelper.quitLoginPage();
                        Toast.makeText(MainActivity.this, "本机号码登录获取token成功,token=" + tokenRet.getToken(), Toast.LENGTH_LONG).show();

                        doraemonManager.getMobileExtendParamsJson(tokenRet.getToken(), null, new MobileExtendParamsService() {
                            @Override
                            public void invoke(String base64EncodedMobileExtendParamsJson, String base64EncodedMobileExtendParamsJsonSign) {
                                Log.d(TAG, "invoke() called with: base64EncodedMobileExtendParamsJson = [" + base64EncodedMobileExtendParamsJson + "], base64EncodedMobileExtendParamsJsonSign = [" + base64EncodedMobileExtendParamsJsonSign + "]");
                            }

                            @Override
                            public void success(boolean success) {
                                Log.d(TAG, "success() called with: success = [" + success + "]");
                            }

                            @Override
                            public void message(String error) {
                                Log.d(TAG, "message() called with: error = [" + error + "]");
                            }
                        });
                        Log.d(TAG, "onTokenSuccess() called with: (tokenRet.getToken() = [" + (tokenRet.getToken() + "]"));
                    }
                } catch (Exception e) {
                    Log.d(TAG, "onTokenSuccess() called with: e) = [" + e + "]");
                    e.printStackTrace();
                    hideLoadingDialog();
                }
            }

            @Override
            public void onTokenFailed(String s) {
                Log.d(TAG, "onTokenFailed() called with: s = [" + s + "]");
                mPhoneNumberAuthHelper.setAuthListener(null);
            }
        };
        mPhoneNumberAuthHelper = PhoneNumberAuthHelper.getInstance(MainActivity.this, tokenResultListener);
        mPhoneNumberAuthHelper.getReporter().setLoggerEnable(true);
        mPhoneNumberAuthHelper.setAuthSDKInfo(AUTH_SECRET);
        mPhoneNumberAuthHelper.checkEnvAvailable(PhoneNumberAuthHelper.SERVICE_TYPE_AUTH);
        mUIConfig = BaseUIConfig.init(MainActivity.this, mPhoneNumberAuthHelper);
        mUIConfig.configAuthPage();
        getLoginToken(5000);
    }




    public void numberAuth(int timeout) {
        mPhoneNumberAuthHelper.setAuthListener(mVerifyListener);
        mPhoneNumberAuthHelper.getVerifyToken(timeout);
    }

    /**
     * 拉起授权页
     *
     * @param timeout 超时时间
     */
    public void getLoginToken(int timeout) {
        mPhoneNumberAuthHelper.getLoginToken(this, timeout);
        showLoadingDialog("正在唤起授权页");
    }

    public void showLoadingDialog(String hint) {
        if (mProgressDialog == null) {
            mProgressDialog = new ProgressDialog(this);
            mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        }
        mProgressDialog.setMessage(hint);
        mProgressDialog.setCancelable(true);
        mProgressDialog.show();
    }

    public void hideLoadingDialog() {
        if (mProgressDialog != null) {
            mProgressDialog.dismiss();
        }
    }
}

7. Crash收集(建议开启)

功能介绍

从v2.10.0版本之后,号码认证SDK支持自己收集SDK内部的Crash问题,方便线上问题排查。客户可以自行选择是否让SDK支持该能力,如果需要支持,则要额外接入SDK包中提供的crashshield-release.aar

集成指南

crashshield-release.aar

implementation 'com.ucweb.wpk:crashsdk-java:3.2.0.1'

混淆配置

增加混淆配置

-keep class com.uc.crashsdk.** { *; }
-keep interface com.uc.crashsdk.** { *; }

注意事项

由于uc的crash收集原理与市面常见crash收集库的原理类似,如果App自身原本就有crash收集能力,建议将自身的crash库放在前面加载,uc的crash不会替换原有的crash收集能力,而是会形成链式传递,且只会收集跟号码认证SDK相关的crash,其他的crash信息不会收集。

8. 手机号码认证授权页面说明

注意

涉及图片路径的参数,仅仅为图片名称(不带路径或后缀名),并且图片需要放置在drawable、drawable-xxhdpi等目录下。

8.1 授权页面设计规范

8.2 弹窗授权页面设计规范(支持横竖屏,以竖屏示意)

注意

请勿遮挡协议栏、一键登录按钮以及掩码或者将字体颜色设置为透明,否则获取不到一键登录token。

8.3 授权页配置说明

8.3.1 授权页导航栏

方法

参数类型

说明

setStatusBarColor

int

设置状态栏颜色(系统版本 5.0 以上可设置)

setLightColor

int

设置状态栏字体颜色(系统版本 6.0 以上可设置黑色、白色)。true 为黑色

setNavColor

int

设置导航栏颜色

setNavText

String

设置导航栏标题文字

setNavTextColor

int

设置导航栏标题文字颜色

setNavTextSize

int

设置导航栏标题文字大小

setNavReturnImgPath

String

设置导航栏返回键图片

setNavReturnHidden

boolean

设置导航栏返回按钮隐藏

setNavHidden

boolean

设置默认导航栏是否隐藏

setStatusBarHidden

boolean

设置状态栏是否隐藏

setStatusBarUIFlag

int

设置状态栏UI属性,View.SYSTEM_UI_FLAG_LOW_PROFILE、View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

setWebViewStatusBarColor

int

设置协议页状态栏颜色(系统版本 5.0 以上可设置),不设置则与授权页设置一致

setWebNavColor

int

设置协议页顶部导航栏背景色,不设置则与授权页设置一致

setWebNavTextColor

int

设置协议页顶部导航栏标题颜色,不设置则与授权页设置一致

setWebNavTextSize

int

设置协议页顶部导航栏文字大小,不设置则与授权页设置一致

webNavReturnImgPath

String

设置协议页导航栏返回按钮图片路径,不设置则与授权页设置一致

setBottomNavColor

int

设置底部虚拟按键背景色(系统版本5.0以上可设置)

8.3.2 授权页Logo

方法

参数类型

说明

setLogoHidden

boolean

隐藏logo

setLogoImgPath

String

设置logo图片

setLogoWidth

int

设置logo控件宽度

setLogoHeight

int

设置logo控件高度

setLogoOffsetY

int

设置logo控件相对导航栏顶部的位移,单位dp

setLogoOffsetY_B

int

设置logo控件相对底部的位移,单位dp

setLogoScaleType

ImageView.ScaleType

设置logo图片缩放模式

8.3.3 授权页Slogan

方法

参数类型

说明

setSloganText

String

设置slogan文字内容

setSloganTextColor

int

设置slogan文字颜色

setSloganTextSize

int

设置slogan文字大小

setSloganOffsetY

int

设置slogan相对导航栏顶部的位移,单位dp

setSloganOffsetY_B

int

设置slogan相对底部的位移,单位dp

8.3.4 授权页号码栏

方法

参数类型

说明

setNumberColor

int

设置手机号码字体颜色

setNumberSize

int

设置手机号码字体大小

setNumFieldOffsetY

int

设置号码栏控件相对导航栏顶部的位移,单位 dp

setNumFieldOffsetY_B

int

设置号码栏控件相对底部的位移,单位dp

setNumberFieldOffsetX

int

设置号码栏相对于默认位置的X 轴偏移量,单位dp

setNumberLayoutGravity

int

设置手机号掩码的布局对齐方式,只支持Gravity.CENTER_HORIZONTAL、Gravity.LEFT、Gravity.RIGHT三种对齐方式

8.3.5 授权页登录按钮

方法

参数类型

说明

setLogBtnText

String

设置登录按钮文字

setLogBtnTextColor

int

设置登录按钮文字颜色

setLogBtnTextSize

int

设置登录按钮文字大小

setLogBtnWidth

int

设置登录按钮宽度,单位dp

setLogBtnHeight

int

设置登录按钮高度,单位dp

setLogBtnMarginLeftAndRight

int

设置登录按钮相对于屏幕左右边缘边距

setLogBtnBackgroundPath

String

设置登录按钮背景图片路径

setLogBtnOffsetY

int

设置登录按钮相对导航栏顶部的位移,单位dp

setLogBtnOffsetY_B

int

设置登录按钮相对底部的位移,单位dp

setLoadingImgPath

String

设置登录loading dialog 背景图片路径

setLogBtnOffsetX

int

设置登录按钮X轴偏移量,如果设置了setLogBtnMarginLeftAndRight,并且布局对齐方式为左对齐或者右对齐,则会在margin的基础上再增加offsetX的偏移量,如果是居中对齐,则仅仅会在居中的基础上再做offsetX的偏移。

setLogBtnLayoutGravity

int

设置登录按钮布局对齐方式,只支持Gravity.CENTER_HORIZONTAL、Gravity.LEFT、Gravity.RIGHT三种对齐方式

8.3.6 授权页隐私栏

授权页面的隐私栏请勿用任何手段进行遮掩,否则会导致号码认证失败。

方法

参数类型

说明

setAppPrivacyOne

String,String

设置开发者隐私条款1名称和URL(名称,url)

setAppPrivacyTwo

String,String

设置开发者隐私条款2名称和URL(名称,url)

setAppPrivacyColor

int,int

设置隐私条款名称颜色(基础文字颜色,协议文字颜色)

setPrivacyOffsetY

int

设置隐私条款相对导航栏顶部的位移,单位dp

setPrivacyOffsetY_B

int

设置隐私条款相对底部的位移,单位dp

setPrivacyState

boolean

设置隐私条款是否默认勾选

setProtocolGravity

int

设置隐私条款文字对齐方式,单位Gravity.xxx

setPrivacyTextSize

int

设置隐私条款文字大小,单位sp

setPrivacyMargin

int

设置隐私条款距离手机左右边缘的边距,单位dp

setPrivacyBefore

String

设置开发者隐私条款前置自定义文案

setPrivacyEnd

String

设置开发者隐私条款尾部自定义文案

setCheckboxHidden

boolean

设置复选框是否隐藏

setUncheckedImgPath

String

设置复选框未选中时图片

setCheckedImgPath

String

设置复选框选中时图片

setVendorPrivacyPrefix

String

设置运营商协议前缀符号,只能设置一个字符,且只能设置<>、()、《》、【】、『』、[]、()中的一个

setVendorPrivacySuffix

String

设置运营商协议后缀符号,只能设置一个字符,且只能设置<>、()、《》、【】、『』、[]、()中的一个

setProtocolLayoutGravity

int

设置隐私栏的布局对齐方式,该接口控制了整个隐私栏(包含checkbox)在其父布局中的对齐方式,而setProtocolGravity控制的是隐私协议文字内容在文本框中的对齐方式

setPrivacyOffsetX

int

设置隐私栏X轴偏移量,单位dp

setLogBtnToastHidden

boolean

设置checkbox未勾选时,点击登录按钮toast是否显示

8.3.7 切换方式控件

方法

参数类型

说明

setSwitchAccHidden

boolean

设置切换按钮点是否可见

setSwitchAccText

String

设置切换按钮文字内容

setSwitchAccTextColor

int

设置切换按钮文字颜色

setSwitchAccTextSize

int

设置切换按钮文字大小

setSwitchOffsetY

int

设置换按钮相对导航栏顶部的位移,单位dp

setSwitchOffsetY_B

int

设置换按钮相对底部的位移,单位dp

8.3.8 页面相关函数

方法

参数类型

说明

setAuthPageActIn

String

设置授权页进场动画

setAuthPageActOut

String

设置授权页退出动画

setScreenOrientation

int

设置屏幕方向,取值为ActivityInfo中的屏幕方向常量值,比如:ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE

setPageBackgroundPath

String

设置授权页背景图,drawable资源的目录,不需要加后缀,比如图片在drawable中的存放目录是res/drawable-xxhdpi/loading.png,则传入参数为"loading",setPageBackgroundPath("loading")。

setDialogWidth

int

设置弹窗模式授权页宽度,单位dp,设置大于0即为弹窗模式

setDialogHeight

int

设置弹窗模式授权页高度,单位dp,设置大于0即为弹窗模式

setDialogOffsetX

int

设置弹窗模式授权页X轴偏移,单位dp

setDialogOffsetY

int

设置弹窗模式授权页Y轴偏移,单位dp

setDialogBottom

boolean

设置授权页是否居于底部

9. 常见问题

1.首次取号时,为什么APP网络通信正常,手机号认证一直失败?

  • 首先检测sim卡有没有欠费,能不能通过移动网络上网。

  • Android 国内某些厂商的系统,wifi 网络权限与移动网络权限是分开管理的,检测APP是否仅仅只有WLAN 网络权限,而移动蜂窝网络权限缺失。

2.checkEnvAvailable函数返回false?

  • 检测是否有插入sim卡

  • 检测蜂窝网络开关是否开启

3. 获取token失败,一般有哪些原因?
  • 手机设置检测网络制式,中国移动支持2G/3G/4G、中国联通支持3G/4G、中国电信支持4G,但各大运营商网络在4G网络成功率较高

  • 接口超时时间是否过短,建议3000~5000ms

  • SIM能否欠费,是否可以蜂窝网络上网。

  • 切换卡的过程中,需要等网络稳定后,再使用认证登录功能。

4.Android双卡手机本机号码登录过程中,本机号码登录逻辑是怎么样的?

  • 使用默认上网卡进行一键登录认证。

5.返回错误码600005,手机终端不安全有哪些原因?

  • ⼿机⽹络是否连接了代理。

  • ⼿机是否处于hook状态,或者安装了相关的hook框架。

  • APP是否处于attached状态。

  • ⼿机是否被root。

  • 是否在模拟器环境中运⾏。

  • APP是否处于调试状态,使⽤ getReporter.setLoggerEnable(true) 可以关闭此项检测。

6.权限问题

若出现权限相关问题,请检查 APP的权限是否申请正常。正常引用 aar,权限会自动 merge。若权限没有 merge,需要添加如下权限。

<uses-permission
    android:name="android.permission.INTERNET" />
<uses-permission
    android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission
    android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission
    android:name="android.permission.CHANGE_NETWORK_STATE" />

7. 非法手机号码

部分试点纯流量卡,未实名的手机号码会返回非法手机号码的情况。

8. VPN报错

用户开启VPN后使用SDK进行本机号码登录的时候,联通号码会出现源IP错误,电信号码会出现800008错误,移动号码会出现103111错误。请关闭VPN或者打开飞行模式再关闭后进行重试。

9. 页面非法修改

添加悬浮窗控件遮挡隐私协议、登录按钮以及掩码,或者将字体颜色设置为透明,sdk回调600005页面非法修改。

10. 当使用移动卡请求本机号码登录不成功,出现以下报错信息时:

ontokenfaild{"code":"600011","msg":"vendorCode:200025, msg:获得的手机授权码失败:{\"resultCode\":\"200025\",\"authType\":\"1\",\"authTypeDes\":\"WIFI下网关鉴权\",\"resultDesc\":\"发生未知错误\"}","requestCode":0,"vendorName":"CMCC"}请检查依赖v4包com.android.support:support-v4版本是否高于25.4.0或者v7包com.android.support:appcompat-v7版本是否高于25.4.0。

11. 当使用移动卡请求本机号码登录不成功,出现以下报错信息时

ontokenfaild{"code":"600011","msg":"vendorCode:200028, msg:获得的手机授权码失败:{\"resultCode\":\"200028\”,\”authType\":\"1\",\"authTypeDes\”:\”网络鉴权\",\"resultDesc\”:\”网络异常\”}”,”requestCode":0,"vendorName":"CMCC"}

  • 请检查清单文件application标签下是否配置了android:usesCleartextTraffic="true",配置android:networkSecurityConfig文件

  • 如果设置了networkSecurityConfig文件,请在networkSecurityConfig文件里面配置

<application
        android:name=".DemoApplication"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:usesCleartextTraffic="true"
        android:networkSecurityConfig="@xml/config"
        android:requestLegacyExternalStorage="true">
</application>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">cmpassport.com</domain>
    </domain-config>
</network-security-config>

12. setSDKAuthSDKInfo的秘钥如何获取?

登录阿里云控制台,进入认证方案管理,点击”秘钥“进行复制,建议维护在APP服务端。此秘钥不是阿里云的AccessKey,AccessSecret。

13. 内存泄漏

1. Toast内存泄漏

  • 协议没勾选点击⼀键登录按钮显示Toast,退出授权⻚⻚出现内存泄漏。解决办法是 AuthUIConfig.setLogBtnToastHidden(true)隐藏默认Toast,根据点击事件的code⾃⼰显示 Toast即可。

  • ⻚⾯⾮法修改,当授权⻚号码栏、⼀键登录Button、协议栏出现重叠或者遮挡时点击⼀键登录按钮,显示Toast⻚ ⾯⾮法修改。解决办法,查看图层解决重叠即可。

2. TokenResultListener内存泄露

  • SDK内部会持有外部设置进来的TokenResultListener,在本机号码登录功能使⽤完毕之后通过 PhoneNumberAuthHelper.setAuthListener(null)将回调置空即可。