# 小程序登录常见问题
请注意
针对微信登录,您也可以使用 FinClip 提供的关联微信小程序登录功能,降低开发成本,快速复用微信登录能力。点击这里了解能力
在微信小程序下,小程序登录功能一般会通过 OpenID
或 UnionID
作为唯一标识,与小程序服务的账号体系进行关联打通,完成用户账户体系的构建与设计。
对于从微信小程序环境迁移到 FinClip 下的小程序,就会遇到如下问题:
- 登录流程如何快速复用?怎样与小程序服务的账号体系关联?
- 如何让微信小程序中的用户数据与 FinClip 的数据打通?双方的账号体系如何关联?
遇到上述问题时,您可通过以下方案尝试解决:
# 1. 登录方案
# 方案一:服务端改造
适用场景:平台方通过 FinClip 构建自有生态,FinClip 环境中运行的都是第三方的小程序(即平台方无法修改小程序代码)。
- 第一步:在 App 中通过自定义 API 注入
wx.login
方法,按照微信小程序的格式返回 code; - 第二步:小程序无需改动,按照在微信端的实现,此时会将 code 发送给开发者的服务器;
- 第三步:开发者服务器进行适配,通过 code 可判断登录来自 FinClip 平台,从 code 中解析出
UserID
,返回自身账户体系的内容即可。
# 方案二:服务端改造(唤起微信授权)
使用场景:平台方通过 FinClip 完成自身功能的拆分,小程序都由平台方自己开发(即平台方可以修改小程序代码)。
- 第一步:SDK 中集成微信开发平台 SDK;
- 第二步:使用自定义 API 注入
wx.login
,唤起微信授权后即可取得返回 code,此时可能需要对 code 拼接唯一标识; - 第三步:小程序无需改动,按照在微信端的实现,此时会将 code 发送给开发者的服务器;
- 第四步:服务端根据 code 唯一标识,调用不同的授权接口取得
OpenID
,查询登录态返回;
限制:由于微信限制不同主体,不同开放平台下的 OpenID
是不一致的,此时小程序需要关联到同一主体,或者同一开放平台(此时唯一标识为 UnionID
)。
# 方案三:小程序改造
适用场景:对小程序进行逻辑修改,当判断 FinClip 环境中时,直接从 wx.login
或者自定义 API( 如 wx.loginFinlip
) 中获取需要的信息(即平台方可以修改小程序代码)。
- APP 使用自定义 API 注入
wx.login
,返回当前 APP 用户的登录态即可。
# 2. 自定义接口实现代码
基于方案三,我们提供了对应的实现代码,供您参考。
# 2.1 APP接入微信登录
若您需要实现APP的微信账号授权登录,请参考微信开放平台的移动应用开发 (opens new window)和文档微信授权登录开发文档 (opens new window)进行实现,若不需要则直接从第2步开始即可。
# 2.2 APP实现自定义注入接口
首先,APP需要参考iOS SDK自定义注入接口和安卓 SDK自定义注入接口两份文档,将账号的授权登录能力注入至SDK中,让小程序能调用,详细说明如下。
Android端: 为了让小程序能够获取到小程序以外的APP数据,需要注册小程序自定义接口,自定义小程序接口具体说明请参照FinClip小程序开放平台-自定义小程序接口、FinClip小程序开放平台-在小程序进程中注册api。
- 自定义login接口
- 自定义Api类,指定api名称为“login”;
public class LoginApi extends AbsApi {
private final static String API_NAME_LOGIN = "login"; // 小程序基础库调用的api名称
@Override
public String[] apis() {
return new String[]{API_NAME_LOGIN};
}
}
- 注册自定义Api;
示例展示了用户授权提示Dialog,需要Activity对象作为Dialog的context参数, 所以需要在小程进程注册自定义Api。
if (FinAppClient.INSTANCE.isFinAppProcess(this)) {
// 小程序进程
// 小程序进程中注册api的方法能获取到小程序所在activity对象,可以用做创建对话框的context参数)
FinAppProcessClient.INSTANCE.setCallback(new FinAppProcessClient.Callback() {
@Override
public List<IApi> getRegisterExtensionApis(@NotNull Activity activity) {
ArrayList<IApi> apis = new ArrayList<>();
apis.add(new LoginApi());
return apis;
}
@Nullable
@Override
public List<IApi> getRegisterExtensionWebApis(@NotNull Activity activity) {
return null;
}
});
return;
}
- 实现自定义Api invoke()方法;
先展示授权提示Dialog(开发者请根据需求决定是否展示授权提示Dialog), 然后再从主进程(App进程)获取用户登录信息。
public class LoginApi extends AbsApi {
@Override
public void invoke(String event, JSONObject param, ICallback callback) {
if (event.equals(API_NAME_LOGIN)) {
showAuthDialog(callback);
}
}
/**
* 显示获取用户登录信息的授权提示对话框
*/
private void showAuthDialog(final ICallback callback) {
// 是否需要显示授权提示对话框请开发者按照产品需求自行处理
new AlertDialog.Builder(activity)
.setTitle("是否同意授权获取用户登录信息?")
.setNegativeButton("拒绝", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
callback.onFail();
}
})
.setPositiveButton("同意", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
loginMainProcess(callback);
}
})
.show();
}
/**
* 从主进程获取用户登录信息
*/
private void loginMainProcess(final ICallback callback) {
// 小程序进程调用主进程,在主进程获取用户信息后返回给小程序进程
FinAppProcessClient.INSTANCE.getAppletProcessApiManager()
.callInMainProcess(API_NAME_LOGIN, null, new FinCallback<String>() {
@Override
public void onSuccess(final String result) {
// 需要在主线程调用callback方法
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
callback.onSuccess(new JSONObject(result));
} catch (JSONException e) {
e.printStackTrace();
callback.onFail();
}
}
});
}
@Override
public void onError(int code, String error) {
callback.onFail();
}
@Override
public void onProgress(int status, String info) {
}
});
}
}
随后在主进程(通常是在Application里面,开发者也可以选择其他合适的位置), 返回用户登录信息给小程序进程。
// 在主进程设置"小程序进程调用主进程"的处理方法
// 开发者也可以选择在主进程其他合适的代码位置设置处理方法
FinAppClient.INSTANCE.getAppletApiManager()
.setAppletProcessCallHandler(new IAppletApiManager.AppletProcessCallHandler() {
@Override
public void onAppletProcessCall(@NotNull String name,
@Nullable String params,
@Nullable FinCallback<String> callback) {
if (callback != null) {
if (name.equals(LoginApi.API_NAME_LOGIN)) {
// 从主进程获取登录信息,返回给小程序进程
// 这里返回的是虚拟的用户登录信息,开发者请从APP里面自行获取用户登录信息
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("userId", "123");
} catch (JSONException e) {
e.printStackTrace();
}
callback.onSuccess(jsonObject.toString());
}
}
}
});
至此,小程序通过自定义Api从APP获取用户登录信息的整个流程就已经完成了。
注意:如果产品需求不需要展示用户授权提示Dialog,建议在主进程(APP进程)注册自定义Api,从而省掉上述在小程序进程调用主进程(APP进程)方法获取数据的过程。
自定义getUserProfile接口
- 自定义接口名称为“getUserProfile”的API。
- 自定义getUserProfile接口流程与自定义login接口流程一致,此处不再赘述。
FinClip还提供了官方的安卓APP Demo 工程和小程序 (opens new window)作为示范,如有需求,您可以前往下载体验。
iOS 端:
- 在原生App中调用sdk的注入api方法,getUserProfile是微信小程序对应的获取用户信息方法名。
[[FATClient sharedClient]registerExtensionApi:@"getUserProfile" handle:^(id param, FATExtensionApiCallback callback) {
//返回用户信息,未登录,可以在这里弹出登录,登录完成后返回用户信息
NSDictionary *userInfo = @{@"nickName":@"张三",@"avatarUrl":@"",@"gender":@1,@"country":@"中国",@"province":@"广东省",@"city":@"深圳",@"language":@"zh_CN"};
NSDictionary *resDic = @{@"userInfo":userInfo};
//回调给小程序用户信息的结果
if (resDic) {
callback(FATExtensionCodeSuccess,resDic);
}else{
callback(FATExtensionCodeFailure,resDic);
}
}];
- 小程序中调用声明的getUserProfile,得到用户信息
wx.getUserProfile({
success: (res) => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
- FinClip还提供了官方的iOS APP Demo 工程和小程序 (opens new window)作为示范,如有需求,您可以前往下载体验。
# 2.3 小程序实现接口的调用
小程序如需支持 login 和 getUserProfile api,需要在 APP 以注入 API 的方式实现,注入 API 的实现步骤可参阅第2步。
需要注意的是,login 和 getUserProfile 为小程序默认注入接口,无需在小程序根目录配置 FinChatConf 即可调通该两个接口。
APP 未注入相关接口时,调用会报错并执行 fail 回调。
- 调起登录接口
wx.login({
param1: '', // 按需添加参数,会透传给 APP 处理
success: (res) => {
console.log(res)
},
fail: (res) => {
console.log(res.errMsg)
}
})
- 调起获取用户信息接口
wx.getUserProfile({
success: (res) => {
console.log('getUserProfile success', res)
},
fail: (res) => {
console.log('getUserProfile fail', res.errMsg)
}
})
# 3. 常见问题
微信官方公告 (opens new window),自 2021 年 12 月 27 日后不再向开发者输出用户昵称与头像信息,您可能需要自行处理获取用户头像与昵称的相关逻辑。
常见的处理逻辑如下:
- 在小程序或小游戏中,不显示用户的头像与昵称,使用其他字段内容代替;
- 在用户进入小程序或小游戏后,随机给用户生成头像与昵称;
- 在用户进入小程序或小游戏后,引导用户自行配置头像,昵称等内容信息。