一.uni-id公用模块环境配置
uni_modules版
1.插件市场导入uni-id公用模块uni_modules版本
2.在uni-config-center公用模块下创建uni-id目录,在uni-id目录下再创建config.json文件配置
uni_modules\uni-config-center\uniCloud\cloudfunctions\common\uni-config-center\uni-id\config.json
…\uniCloud\cloudfunctions\common\uni-config-center\uni-id\config.json
3.cloudfunctions/common文件上传更新
4.在要使用uni-id的云函数右键选择管理公共模块依赖添加uni-id到云函数,添加依赖后需要重新上传该云函数
5.创建uni-id-users、opendb-verify-codes等集合,也可以在云控制台创建
config.json是一个标准json文件,不支持注释
{
"passwordSecret": "passwordSecret-demo",
"tokenSecret": "tokenSecret-demo",
"tokenExpiresIn": 7200,
"tokenExpiresThreshold": 600,
"bindTokenToDevice": false,
"passwordErrorLimit": 6,
"passwordErrorRetryTime": 3600,
"autoSetInviteCode": false,
"forceInviteCode": false,
"removePermissionAndRoleFromToken": false,
"app-plus": {
"tokenExpiresIn": 2592000,
"oauth": {
"weixin": {
"appid": "weixin appid",
"appsecret": "weixin appsecret"
},
"qq": {
"appid": "qq appid",
"appsecret": "qq appsecret"
},
"apple": {
"bundleId": "your bundleId"
}
}
},
"mp-weixin": {
"tokenExpiresIn": 259200,
"oauth": {
"weixin": {
"appid": "weixin appid",
"appsecret": "weixin appsecret"
}
}
},
"mp-qq": {
"tokenExpiresIn": 259200,
"oauth": {
"qq": {
"appid": "qq appid",
"appsecret": "qq appsecret"
}
}
},
"mp-alipay": {
"tokenExpiresIn": 259200,
"oauth": {
"alipay": {
"appid": "alipay appid",
"privateKey": "alipay privateKey",
"keyType": "PKCS8"
}
}
},
"service": {
"sms": {
"name": "your app name",
"codeExpiresIn": 180,
"smsKey": "your sms key",
"smsSecret": "your sms secret"
},
"univerify": {
"appid": "your appid",
"apiKey": "your apiKey",
"apiSecret": "your apiSecret"
}
}
}
二.持久化保存token及其过期时间
开发者应在用户登录/注册成功后持久化保存token及其过期时间。同样的在用户登出之后应删除保存的token及其过期时间。
客户端保存token及其有效期
在用户登录/注册成功后应在storage内保存用户的token及其过期时间,示例代码如下:
按照以下格式保存的token,在调用云函数的时候会自动携带.
uni.setStorageSync('uni_id_token', token)
uni.setStorageSync('uni_id_token_expired', tokenExpired)
客户端删除token及其有效期
在用户登出成功之后应删除持久化存储的token及其过期时间,示例代码如下:
uni.removeStorageSync('uni_id_token')
uni.removeStorageSync('uni_id_token_expired')
三.index.vue+云函数userCenter实现注册\登录\改密等操作
// 云函数userCenter的代码
const uniID = require('uni-id')
const uniCaptcha = require("uni-captcha")
exports.main = async function(event, context) {
let res = {}
let release = ["register", "login", "logout", "createCaptcha", "verifyCaptcha", "refreshCaptcha"];//不需要token验证的操作
let payload = {}
let {
uniIdToken,//客服端提交的时候会自动携带 格式:uni_id_token;uni_id_token_expired
action,
param
} = event
param = param ? param : {}
if (release.indexOf(action) === -1) {
if (!uniIdToken) {
res = {
code: "740",
msg: "缺少token"//阻止未登录或者token过期的操作
}
return res
}
payload = await uniID.checkToken(uniIdToken)
if (payload.code === 0) {
console.log("token校验通过"); //payload.uid
param.uid = payload.uid
} else {
console.log("token校验未通过");//阻止未登录或者token过期的操作
return payload
}
}
switch (action) {
case "createCaptcha":
res = await uniCaptcha.create(param)
break;
case "verifyCaptcha":
res = await uniCaptcha.verify(param)
break;
case "refreshCaptcha":
res = await uniCaptcha.refresh(param)
break;
case "register":
res = await uniID.register(param)
break;
case "login":
res = await uniID.login(param)
break;
case "logout":
res = await uniID.logout(uniIdToken)
break;
case "modifyPW":
res = await uniID.updatePwd(param);
break;
case "modifyAvatar":
res = await uniID.setAvatar(param)
break;
case "modifyNickname":
res = await uniID.updateUser(param)
break;
case "getUserInfo":
res = await uniID.getUserInfo(param)
break;
default:
res = {
code: "433",
msg: "请求非法"//过滤非法请求
}
}
return res
}
<template>
<view class="content">
<image class="avatarImg" :src="formData.avatar" mode="aspectFill"></image>
<text>{{nickname}}</text>
<uni-easyinput v-model="formData.username" placeholder="用户名" />
<uni-easyinput v-model="formData.password" placeholder="密码" />
<view class="captchaImgBox">
<uni-easyinput style="margin-top: 6rpx;" v-model="captcha" placeholder="请输入验证码" />
<image class="captchaImg" :src="formData.captcha" mode="aspectFit" @click="refreshCaptcha()">
</image>
</view>
<uni-easyinput v-model="formData.newpassword" placeholder="新密码" />
<uni-easyinput v-model="formData.nickname" placeholder="昵称" />
<button type="default" @click="submit('register')">注册</button>
<button type="default" @click="submit('login')">登录</button>
<button type="default" @click="submit('logout')">退出</button>
<button type="default" @click="submit('modifyPW')">修改密码</button>
<button type="default" @click="submit('modifyAvatar')">修改头像</button>
<button type="default" @click="submit('modifyNickname')">修改昵称</button>
</view>
</template>
<script>
export default {
data() {
return {
nickname: "nickname",
captcha: "", //验证码文本
formData: {
username: "",
password: "",
newpassword: "",
nickname: "",
gender: 1, //男1 女2
mobile: "",
email: "",
avatar: "",
comment: "",
captcha: "" //图片数据
}
}
},
onLoad() {},
onReady() {
this.createCaptcha()
},
methods: {
createCaptcha() {
uniCloud.callFunction({
name: "userCenter",
data: {
action: "createCaptcha",
param: {
scene: "login",
noise: 0
}
}
}).then(e => {
// console.log(e)
this.formData.captcha = e.result.captchaBase64
})
},
refreshCaptcha() {
uniCloud.callFunction({
name: "userCenter",
data: {
action: "refreshCaptcha",
param: {
scene: "login",
noise: 0
}
}
}).then(e => {
// console.log(e)
this.formData.captcha = e.result.captchaBase64
this.captcha = ""
})
},
async verifyCaptcha() {
return await uniCloud.callFunction({
name: "userCenter",
data: {
action: "verifyCaptcha",
param: {
scene: "login",
captcha: this.captcha
}
}
})
},
req(action, param) {
return new Promise((resolve, reject) => {
uniCloud.callFunction({
name: "userCenter",
data: {
action,
param
},
success: (e) => {
resolve(e.result)
},
fail: (e) => {
resolve(e)
}
})
})
},
async submit(action) {
uni.showLoading({})
console.log(action);
let res = {}
switch (action) {
case "register":
res = await this.verifyCaptcha()
console.log(res.result.code === 0 ? "注册成功" : res.result.msg);
if (!res.result.code) {
res = await this.req(action, {
username: this.formData.username,
password: this.formData.password
})
}
this.refreshCaptcha()
break;
case "login":
res = await this.verifyCaptcha()
console.log(res);
console.log(res.result.code === 0 ? "登录成功" : res.result.msg);
if (res.result.code === 0) {
res = await this.req(action, {
username: this.formData.username,
password: this.formData.password
})
if (res.code === 0) {
uni.setStorageSync('uni_id_token', res.token)
uni.setStorageSync('uni_id_token_expired', res.tokenExpired)
this.req("getUserInfo", {
fields: ["nickname", "avatar"]
}).then(e => {
this.formData.avatar = e.userInfo.avatar
this.nickname = e.userInfo.nickname
}).catch(err => {
console.log(err);
})
}
}
this.refreshCaptcha()
break;
case "logout":
res = await this.req(action, {
username: this.formData.username,
password: this.formData.password,
})
console.log(res.code === 0 ? "退出成功" : res.errMsg);
this.formData.avatar = "",
this.nickname = "nickname"
break;
case "modifyPW":
res = await this.req(action, {
username: this.formData.username,
oldPassword: this.formData.password,
newPassword: this.formData.newpassword
})
console.log(res.code === 0 ? "密码修改成功" : res.errMsg);
break;
case "modifyAvatar":
res = await uniCloud.chooseAndUploadFile({
count: 1,
type: "image",
}).then(e => {
let url = e.tempFiles[0].url
console.log(url);
this.formData.avatar = url
this.req(action, {
avatar: this.formData.avatar
})
.then(e => {
console.log(e.code === 0 ? "头像修改成功" : e.errMsg);
}).catch(err => {
console.log(err);
})
}).catch(err => {
console.log(err);
})
break;
case "modifyNickname":
res = await this.req(action, {
nickname: this.formData.nickname
})
console.log(res.code === 0 ? "昵称修改成功" : res.errMsg);
if (res.code === 0) {
this.nickname = this.formData.nickname
}
break;
default:
console.log("default");
}
uni.hideLoading()
}
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
.avatarImg {
width: 250rpx;
height: 270rpx;
border: #007AFF solid 1px;
}
.captchaImgBox {
width: 750rpx;
display: flex;
flex-direction: row;
justify-content: flex-start;
}
.captchaImg {
width: 220rpx;
height: 80rpx;
border: #007AFF solid 1px;
}
</style>
四.token校验
一个校验客户端用自带的***uniIdToken***发起请求(uniCloud.callFunction),获得用户的uid、token、token的过期时间、角色、权限、用户信息(uni-id-users全部字段)的API。
这是非常高频且重要的API通常用于换取操作当前云函数的用户***uid***。
思考
如果你并没有服务端开发经验,可能会想:为什么需要通过token去换取用户Id,而不是让客户端直接传递用户Id更方便?
token===>>>uid
// 云函数list-news代码
const uniID = require('uni-id')
exports.main = async function(event,context) {
const payload = await uniID.checkToken(event.uniIdToken)
const {
code,
token,
tokenExpired
} = payload
if(code) { // code不为0代表token校验未通过
return payload
}
// 其他业务代码
return {
token,
tokenExpired
}
}
五.更新用户信息
// 云函数代码
const uniID = require('uni-id')
exports.main = async function(event,context) {
payload = await uniID.checkToken(event.uniIdToken)
if (payload.code) {
return payload
}
const res = await uniID.updateUser({
uid: payload.uid,
nickname: 'user nickname'
})
return res
}
六.获取用户信息
// 云函数代码
const uniID = require('uni-id')
exports.main = async function(event,context) {
payload = await uniID.checkToken(event.uniIdToken)
if (payload.code) {
return payload
}
const res = await uniID.getUserInfo({
uid: payload.uid,
field: ['mobile']
})
return res
}
//根据token获取用户信息
// 云函数代码
const uniID = require('uni-id')
exports.main = async function(event,context) {
const res = await uniID.getUserInfoByToken(event.uniIdToken)
return res
// res = {
// uid: 'xxx',
// role: [],
// permission: []
// }
}
七.过滤拦截用户请求
// 拦截用户某些请求
// 构建放行数组
// 拦截未登录以及登录失效
// 过滤非法请求
'use strict';
const uniIDs = require('uni-id')
exports.main = async (event, context) => {
let res = {}; //响应
let params = event.params ?event.params:{}; //接收请求数据
const release = ['login', 'register', 'logout'];// 构建放行数组
if (release.indexOf(event.action) == -1) {
if (!event.uniIdToken) {
res = {
code: 403,
message: "未携带token"
}
return res;// 拦截未登录以及登录失效
} else {
let payload = await uniID.checkToken(event.uniIdToken, {});
if (payload.code === 0) {
params.uid = payload.uid;
} else {
res = payload;
return res;// 拦截未登录以及登录失效
}
}
}
switch (event.action) {
...进行对应操作
default: {
res = {
code: 402,
message: "请求非法"// 过滤非法请求
}
break;
}
}
//返回数据给客户端
return res;
};
八.微信登录
注意
0.HBuilder X设置菜单中配置微信开发者工具路径
1.微信开发者工具中,设置—安全设置—开启服务端口
2.需要在uni-id的config.json内app-plus对应的微信登录信息内配置appid和appsecret
3.manifest.json中配置微信appid
4.登录成功之后会返回token、tokenExpired,在获取token之后应进行持久化存储
uni.setStorageSync('uni_id_token', res.result.token)
uni.setStorageSync('uni_id_token_expired', res.result.tokenExpired)
5.打包并使用自定义基座(注意一定要在manifest.json填写微信appid后再制作自定义基座)
APP微信登录详细流程
===>uni.login
===>code
===>调用云函数uniID.loginByWeixin({code})
===>token
===>本地保存token
===>uni.getUserInfo()
//云函数wxLogin
'use strict';
const uniID = require("uni-id")
exports.main = async (event, context) => {
return await uniID.loginByWeixin({
code: event.code
})
};
//本地index.vue
<template>
<view class="content">
<image class="logo" :src="userInfo.avatarUrl"></image>
<view class="text-area">
<text class="title">{{userInfo.nickName}}</text>
</view>
<button type="default" @click="login()">Login</button>
</view>
</template>
<script>
export default {
data() {
return {
userInfo: {}
}
},
onLoad() {
},
methods: {
getWXcode() {
return new Promise((resolve, reject) => {
uni.login({
provider: "weixin",
success: (e) => {
resolve(e.code)
},
fail: () => {
reject(new Error("微信登录失败"))
}
})
})
},
login() {
this.getWXcode().then(code => {
console.log("code:" + code);
uniCloud.callFunction({
name: "wxLogin",
data: {
code
}
}).then(res => {
console.log(res.result.code === 0 ? "微信登录成功" : "微信登录失败");
if (res.result.code === 0) {
uni.setStorageSync('uni_id_token', res.result.token)
uni.setStorageSync('uni_id_token_expired', res.result.tokenExpired)
let {
openid,
} = res.result
console.log("openid:" + openid);
return new Promise((resolve, reject) => {
uni.getUserInfo({
provider: "weixin",
success: (e) => {
resolve(e)
},
fail: () => {
reject(new Error("getUserInfo失败"))
}
})
})
} else {
return new Error("微信登录失败")
}
}).then(e => {
let {
userInfo
} = e;
this.userInfo = userInfo
console.log(userInfo);
const version=getApp().globalData.version
console.log(version);
}).catch(err => console.log(err))
})
}
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>