uni-app 是一个跨端开发的前端框架,可以使用一套代码同时开发 IOS、安卓、小程序的前端,是快速开发移动应用的不错的选择。
在现实应用场景中,一个用户在使用当前移动应用时可能会用 IOS、安卓或小程序的任意一种。大部分情况下,这个用户无论在使用何种设备环境下,他都是唯一标识的用户,设备分区那些应用除外(例如腾讯的部分游戏)。
现有方案
那么如何标识出一个用户在不同设备环境下均为同一个用户呢? 通常有以下几种方案:
- 使用用户名和密码作为登录系统:该方案需要用户主动注册并设置用户名和密码,那么他在不同设备环境下,使用自己账户登录即是唯一的。
- 使用第三方登录作为登录系统:例如微信授权登录,在 IOS 和安卓应用中均可以实现跳转微信授权,而小程序端如果只是微信小程序,那么在同一主体下理论上可以通过 unionid 标识出该用户,但支付宝或其他类型的小程序则无法应用该方案。
- 使用手机号作为登录系统:用户手机号可以作为唯一标识用户的方法,且使用手机号授权的方式,会比方案一使用账号密码的方案更加方便。
综上分析,第三种方案可能是最为普遍的方案,本文会以第三种方案使用手机号授权为例,来详细地阐述该方案的实行过程。
IOS、安卓一键登录
APP 端在使用手机号授权时会比较方便,因为 uni-app 提供了一键登录方案,可以按照官方给出的 一键登录文档 进行开发,笔者在该过程中排了一些坑,可以按照下文操作。
登录界面
第一步,最好是能先把这个一键登录的界面先能实现出来
文档中也提供了 uni.login()
方式即可
uni.login({
provider: 'univerify',
univerifyStyle: {
fullScreen: true
}
})
可以通过修改 univerifyStyle
属性个性化定制页面,具体也可以参考文档中 univerifyStyle 数据结构 部分。
authApp() {
uni.login({
provider: 'univerify',
univerifyStyle: {
fullScreen: true,
otherLoginButton: {
visible: false
},
icon: {
path: "static/logo.png" },
},
success: res => {
console.log(res)
if (res.errMsg === 'login:ok') {
// 登录处理
}
}
})
}
云函数换取手机号
在文档中可以发现,其提供了三种方式换取手机号,在点击授权后会收到一个 access_token
和一个 openid
,此时需要使用 access_token
换取手机号。
简单地讲,它有三种换取手机号的方案:
- 前端单纯使用云函数调用
- 前端将参数
access_token
提交给云函数 - 前端将参数
access_token
提交给自己的服务器,自己的服务器再调用云函数
这三种方案,可以发现一个共同的特点,就是没法离开云函数,开始笔者也想找其他方案,但未果。
综合考虑官方给出的三种方案,如果不实用其提供的云数据库的话,第 3 种方案会是一个好的选择,但官方还提到了 URL 化,实测其实可以不需要。
开发者配置
写云函数之前需要先进入 开发者中心 进行配置。
首先需要创建一个应用,如果你的应用已经创建则这部无需重复创建。
开通一键登录,开通后会生成 ApiKey
和 ApiSecret
,后续在云开发时会使用到。
顺便再添加应用,这部分是在打包后需要用到的,一般在生成签名时需要注意,可以参考 Android 签名证书生成 ,这个比较详细,按他的方案下来基本可以成功。
云函数配置及编写
右键项目创建云开发环境
创建后再右键关联云服务空间
点击新建
跳转到云服务空间后点击创建
返回到 IDE 中,选择刚刚创建的云空间并关联
新建一个云函数
按如下代码编写一个换取手机号的云函数,此处没有按照官方的方式使用 URL 化,因为云函数是由服务器后端接口去调用的,不对外暴露,无需再做其他复杂的操作。
修改 appid
、apiKey
、apiSecret
。
'use strict';
exports.main = async (event) => {
const params = event.queryStringParameters
const {
access_token,
openid
} = params
const res = await uniCloud.getPhoneNumber({
provider: 'univerify',
appid: '__UNI__xxx',
apiKey: 'xxx',
apiSecret: 'xxx',
access_token: access_token,
openid: openid
})
return res
}
上传写好的云函数
进入云函数控制台,配置这个云函数的路径,自定义即可。
最后可以测试一下, uni.login()
后会打印出参数,之后将参数通过 PostMan
传递给你的云函数,如果成功返回手机号,则可以继续下面的步骤了。
Java 后端调用云函数
后端接受前端传来的,access_token
和 openid
,之后通过后端去调用云函数后,最后可以将手机号和 openid 进行用户信息入库。
入库后,后续的登录直接可以通过 openid
去查找用户,因为 openid
是唯一的,无需再调用云函数换取手机号,毕竟换一次需要 2 分钱呢。
以 Java 为例,调用接口可以使用 RestTemplate
、HttpClient
或其他工具,笔者用的是一个开源工具 http-request 属实非常的方便。
@Component
public class UniCloud {
@Value("${unicloud-url}")
private String uniCloudUrl;
/**
* UniCloud 获取手机号
*/
public String getPhoneNumber(String accessToken, String openid) {
String url = this.uniCloudUrl + "auth?access_token=" + accessToken + "&openid=" + openid;
String res = HttpRequest.get(url).body();
Gson gson = new Gson();
JsonObject jsonObject = gson.fromJson(res, JsonObject.class);
if (jsonObject.has("success") && jsonObject.get("success").getAsBoolean()) {
return jsonObject.get("phoneNumber").getAsString();
}
throw new UniCloudAPIError();
}
}
最后还有一点需要注意,后面是使用小程序的方案登录,如果在通过上述 APP 的 openid 没有找到用户时,还是需要换取一次手机号的,因为这个用户可能已经通过小程序注册了,并且在这一次请求中把 APP 的 openid 也入库。
小程序手机号登录
在结束了 APP 一键登录授权后,还需要实现一下小程序的手机号登录,因为一键登录,仅仅适用于 APP,不适用于小程序。
登录界面
类似的可以做一个和一键登录差不多的页面,此处按钮需要绑定获取手机号的事件。
调用后端接口
此接口我还会传一个 openidCode
,这个 openidCode
其实就是小程序 login() 后返回的 code
,一并传到后端可以获得用户的 openid
。
export default {
data() {
return {
appName: null,
openidCode: null
}
},
onShow() {
uni.login({
success: res => {
this.openidCode = res.code
}
})
},
methods: {
getphonenumber(e) {
uni.showLoading({
title: '正在授权中...'
})
const data = {
'code': e.detail.code,
'openid_code': this.openidCode
}
// 此处使用 uni.request 返送请求到后端,传的参数为 data
}
}
}
Java 后端换取手机号和 openid
小程序后端换取手机号和 openid 的方法比较多,这里推荐使用 WxJava 一个可以直接调用微信小程序、微信公众号等 API 接口的工具,也是相当好用。
@Component
public class WechatMp {
private final WxMaService wxMaService;
@Autowired
public WechatMp(WxMaService wxMaService) {
this.wxMaService = wxMaService;
}
/**
* 获取手机号
*/
public String getPhoneNumber(String code) {
try {
WxMaPhoneNumberInfo newPhoneNoInfo = this.wxMaService.getUserService().getNewPhoneNoInfo(code);
return newPhoneNoInfo.getPurePhoneNumber();
} catch (WxErrorException e) {
throw new WxAPIError();
}
}
/**
* 获取openid
*/
public String getOpenid(String code) {
try {
return this.wxMaService.getUserService().getSessionInfo(code).getOpenid();
} catch (WxErrorException e) {
throw new WxAPIError();
}
}
}
最后,就写到这里吧,过程中还省略了前端发送请求,后端接受请求的方法,可以自行根据业务补充。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于