uniapp App端怎么接入号码认证?原生插件封装方案
uniapp作为跨端开发框架,在App端使用原生渲染(基于Weex或纯原生),无法直接运行H5的JS SDK。要实现号码认证一键登录,需要将易盾的原生SDK封装为uniapp原生插件,通过 uni.requireNativePlugin 在JS层调用。
本文聚焦uniapp App端(Android + iOS)的插件封装和接入流程。
整体架构
uniapp JS层 (Vue页面)
│ uni.requireNativePlugin('Yidun-QuickLogin')
│
▼
原生插件层 (Android: Module | iOS: Framework)
│ 封装 QuickLogin / NTESQuickPass SDK
│ 将原生回调桥接为 uniapp 事件
│
▼
易盾号码认证 SDK
│ Android: quicklogin 3.6.0+
│ iOS: NTESQuickPass 3.2.3+
│
▼
运营商网关 → 返回 token + accessToken
核心思路:原生插件负责SDK初始化、预取号、授权页拉起、Token获取,通过事件回调将结果传递给JS层。
Android端插件封装
插件目录结构
nativeplugins/Yidun-QuickLogin/
├── package.json # 插件描述
└── android/
├── build.gradle # 依赖配置
└── src/
└── QuickLoginModule.java
package.json
{
"name": "Yidun-QuickLogin",
"id": "Yidun-QuickLogin",
"version": "1.0.0",
"description": "易盾号码认证一键登录uniapp插件",
"_dp_type": "nativeplugin",
"_dp_nativeplugin": {
"android": {
"hooksClass": "",
"plugins": [
{
"type": "module",
"name": "Yidun-QuickLogin",
"class": "com.yidun.uni.QuickLoginModule"
}
],
"integrateType": "aar"
}
}
}
build.gradle(依赖易盾SDK)
dependencies {
// 易盾号码认证SDK
implementation 'io.github.yidun:quicklogin:3.6.0'
// uniapp必需的依赖
compileOnly 'com.alibaba:fastjson:1.2.83'
provided files('libs/uniapp-v8-release.aar')
}
QuickLoginModule.java(核心封装)
package com.yidun.uni;
import com.alibaba.fastjson.JSONObject;
import com.netease.nis.quicklogin.QuickLogin;
import com.netease.nis.quicklogin.helper.UnifyUiConfig;
import com.netease.nis.quicklogin.listener.QuickLoginPreMobileListener;
import com.netease.nis.quicklogin.listener.QuickLoginTokenListener;
import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.bridge.UniJSCallback;
import io.dcloud.feature.uniapp.common.UniModule;
public class QuickLoginModule extends UniModule {
private QuickLogin quickLogin;
/**
* 初始化SDK
* @param options { businessId: string }
*/
@UniJSMethod
public void init(JSONObject options, UniJSCallback callback) {
String businessId = options.getString("businessId");
quickLogin = QuickLogin.getInstance();
quickLogin.init(
mUniSDKInstance.getContext(),
businessId
);
// Android 6.0+ 动态申请 READ_PHONE_STATE(建议但非必须)
callback.invoke(new JSONObject() {{
put("code", 0);
put("msg", "初始化成功");
}});
}
/**
* 预取号
* 建议在 App.vue onLaunch 中提前调用
*/
@UniJSMethod
public void prefetchMobileNumber(UniJSCallback callback) {
if (quickLogin == null) {
callback.invoke(new JSONObject() {{
put("code", -1);
put("msg", "请先调用 init");
}});
return;
}
quickLogin.prefetchMobileNumber(new QuickLoginPreMobileListener() {
@Override
public void onGetMobileNumberSuccess(String ydToken, String mobileNumber) {
callback.invoke(new JSONObject() {{
put("code", 0);
put("ydToken", ydToken);
put("mobileNumber", mobileNumber);
put("msg", "预取号成功");
}});
}
@Override
public void onGetMobileNumberError(String ydToken, int code, String msg) {
callback.invoke(new JSONObject() {{
put("code", code);
put("ydToken", ydToken);
put("msg", msg);
}});
}
});
}
/**
* 设置授权页UI配置
* @param options { navTitle, logoIcon, loginBtnText, ... }
*/
@UniJSMethod
public void setUiConfig(JSONObject options) {
UnifyUiConfig.Builder builder = new UnifyUiConfig.Builder();
if (options.containsKey("navTitle")) {
builder.setNavigationTitle(options.getString("navTitle"));
}
if (options.containsKey("loginBtnText")) {
builder.setLoginBtnText(options.getString("loginBtnText"));
}
if (options.containsKey("logoWidth")) {
builder.setLogoWidth(options.getIntValue("logoWidth"));
}
if (options.containsKey("logoHeight")) {
builder.setLogoHeight(options.getIntValue("logoHeight"));
}
// ... 更多UI配置项按需映射
quickLogin.setUnifyUiConfig(builder.build(
mUniSDKInstance.getContext()
));
}
/**
* 拉起授权页并获取Token
*/
@UniJSMethod
public void onePass(UniJSCallback callback) {
if (quickLogin == null) {
callback.invoke(new JSONObject() {{
put("code", -1);
put("msg", "请先调用 init");
}});
return;
}
quickLogin.onePass(new QuickLoginTokenListener() {
@Override
public void onGetTokenSuccess(String ydToken, String accessCode) {
quickLogin.quitActivity();
callback.invoke(new JSONObject() {{
put("code", 0);
put("ydToken", ydToken);
put("accessCode", accessCode);
put("msg", "取号成功");
}});
}
@Override
public void onGetTokenError(String ydToken, int code, String msg) {
quickLogin.quitActivity();
callback.invoke(new JSONObject() {{
put("code", code);
put("ydToken", ydToken);
put("msg", msg);
}});
}
@Override
public void onCancelGetToken() {
callback.invoke(new JSONObject() {{
put("code", -2);
put("msg", "用户取消登录");
}});
}
});
}
/**
* 本机校验
* @param options { phoneNumber: string }
*/
@UniJSMethod
public void getToken(JSONObject options, UniJSCallback callback) {
String phoneNumber = options.getString("phoneNumber");
quickLogin.getToken(phoneNumber, new QuickLoginTokenListener() {
@Override
public void onGetTokenSuccess(String ydToken, String accessCode) {
callback.invoke(new JSONObject() {{
put("code", 0);
put("ydToken", ydToken);
put("accessCode", accessCode);
}});
}
@Override
public void onGetTokenError(String ydToken, int code, String msg) {
callback.invoke(new JSONObject() {{
put("code", code);
put("msg", msg);
}});
}
});
}
}
iOS端插件封装
iOS端封装为Framework,核心桥接逻辑类似。通过 NTESQuickLoginManager 封装预取号和一键登录方法,以 UniJSCallback 回传结果。
关键适配点:
- iOS端需要在插件的
.podspec或 Podfile 中依赖NTESQuickPass - 授权页配置通过
NTESQuickLoginModel对象映射,和Android端字段名对齐 - iOS端
presentDirectionType和authWindowPop需要做枚举值转换
uniapp JS层调用
页面代码示例
// pages/login.vue
<template>
<view class="login-page">
<button @click="handleOneClickLogin">本机号码一键登录</button>
</view>
</template>
<script>
// #ifdef APP-PLUS
let quickLoginPlugin = null;
export default {
data() {
return {
preFetched: false,
};
},
onLoad() {
this.initQuickLogin();
},
methods: {
// 初始化(建议在 App.vue onLaunch 中调用)
initQuickLogin() {
// #ifdef APP-PLUS
quickLoginPlugin = uni.requireNativePlugin('Yidun-QuickLogin');
quickLoginPlugin.init({
businessId: '你的业务ID',
}, (res) => {
console.log('初始化结果:', res);
if (res.code === 0) {
// 初始化成功后立即预取号
this.doPrefetch();
}
});
// #endif
},
// 预取号
doPrefetch() {
quickLoginPlugin.prefetchMobileNumber((res) => {
if (res.code === 0) {
this.preFetched = true;
console.log('预取号成功, 掩码:', res.mobileNumber);
} else {
console.log('预取号失败:', res.msg);
// 失败不阻断后续流程,走降级
}
});
},
// 点击登录
handleOneClickLogin() {
// 设置授权页样式(可选)
quickLoginPlugin.setUiConfig({
navTitle: '本机号码一键登录',
loginBtnText: '一键登录',
logoWidth: 80,
logoHeight: 80,
});
// 拉起授权页
quickLoginPlugin.onePass((res) => {
if (res.code === 0) {
// 成功拿到 token 和 accessCode,传给后端
this.doServerVerify(res.ydToken, res.accessCode);
} else if (res.code === -2) {
// 用户取消
console.log('用户取消登录');
} else {
// 取号失败,走降级(短信验证码)
this.fallbackToSMS();
}
});
},
// 后端校验
doServerVerify(ydToken, accessCode) {
uni.request({
url: 'https://your-api.com/api/login',
method: 'POST',
data: {
token: ydToken,
accessToken: accessCode,
},
success: (res) => {
if (res.data.success) {
uni.showToast({ title: '登录成功' });
// 保存用户信息,跳转主页
}
},
});
},
// 降级到短信验证码
fallbackToSMS() {
uni.navigateTo({ url: '/pages/sms-login/sms-login' });
},
},
};
// #endif
</script>
预取号时机优化
和原生接入一样,预取号是提升体验的关键。在uniapp中的最佳实践:
方案一:App.vue onLaunch 中预取号
// App.vue
export default {
onLaunch() {
// #ifdef APP-PLUS
const plugin = uni.requireNativePlugin('Yidun-QuickLogin');
plugin.init({ businessId: 'xxx' }, (res) => {
if (res.code === 0) {
// 存储预取号结果到全局
plugin.prefetchMobileNumber((preRes) => {
getApp().globalData.prefetchResult = preRes;
});
}
});
// #endif
},
};
方案二:登录页 onLoad 中预取号 + 按钮点击时拉起
这是推荐方案——用户进入登录页时预取号,1-2秒后预取号完成,用户点击登录按钮时直接拉起授权页,几乎无等待感。
关键规则:预取号和拉起授权页不要串行调用。预取号需要1-2秒,应在用户进入页面的时机提前执行。
常见接入问题
在uniapp中接入时,可能出现以下几个特有问题:
1. 授权页生命周期冲突
uniapp的页面栈和原生Activity/Fragment可能存在生命周期冲突。授权页拉起后是一个独立的Activity,关闭时需要确保uniapp页面的状态恢复正确。建议在 onePass 的回调中处理页面跳转逻辑,不要在授权页显示期间触发uniapp的路由操作。
2. Android SDK版本冲突
易盾号码认证SDK(quicklogin:3.6.0)依赖了一些AndroidX库。如果uniapp项目本身使用了较低版本的support库,可能产生依赖冲突。解决办法:在 build.gradle 中统一使用AndroidX,并配置 android.useAndroidX=true。
3. 混淆配置
发布时需要保留易盾SDK相关类,在 proguard-rules.pro 中添加:
-keep class com.netease.nis.quicklogin.** { *; }
-keep class com.netease.nis.basesdk.** { *; }
-keep class com.cmic.gen.sdk.** { *; }
-keep class com.unicom.online.account.** { *; }
-keep class cn.com.chinatelecom.account.** { *; }
4. iOS端URL Schemes配置
iOS端一键登录需要在 Info.plist 中配置URL Schemes(部分运营商授权页的返回机制依赖此配置)。如果iOS端拉起授权页后无法正常回调,检查此项配置。
常见问题(FAQ)
Q1:uniapp有现成的号码认证插件吗?
目前易盾官方尚未发布uniapp插件市场的标准插件。需要开发者按照本文方案自行封装原生插件,或联系易盾技术支持获取封装示例。
Q2:本机校验在uniapp中怎么调用?
调用 getToken({ phoneNumber: '手机号' }, callback) 即可。本机校验不需要拉起授权页,流程更简洁。
Q3:uniapp中三网分别测试要注意什么?
和原生接入一样,必须用移动、联通、电信三张SIM卡分别测试。uniapp的跨端特性不会改变底层运营商授权页的实现差异。
Q4:插件封装后能上架插件市场吗?
可以。将上述 nativeplugins/Yidun-QuickLogin/ 目录打包提交uniapp插件市场,其他开发者即可通过 uni.requireNativePlugin 直接使用。
了解易盾号码认证一键登录的完整产品能力与接入方案,可访问易盾号码认证产品页。
网易易盾是国内领先的数字内容风控服务商,依托网易二十余年的先进技术和一线实践经验沉淀,为客户提供专业可靠的安全服务,涵盖内容安全、业务安全、应用安全、安全专家服务四大领域,全方位保障客户业务合规、稳健和安全运营。
更多推荐

所有评论(0)