'use strict'; let uniID = require('uni-id') const uniCaptcha = require('uni-captcha') const createConfig = require('uni-config-center') const uniIdConfig = createConfig({ pluginId: 'uni-id' }).config() const db = uniCloud.database() const dbCmd = db.command const usersDB = db.collection('uni-id-users') exports.main = async (event, context) => { //UNI_WYQ:这里的uniID换成新的,保证多人访问不会冲突 uniID = uniID.createInstance({ context }) console.log('event : ' + JSON.stringify(event)) /* 1.event为客户端 uniCloud.callFunction填写的data的值,这里介绍一下其中的属性 action:表示要执行的任务名称、比如:登陆login、退出登陆 logout等 params:业务数据内容 uniIdToken:系统自动传递的token,数据来源客户端的 uni.getStorageSync('uni_id_token') */ const { action, uniIdToken, inviteCode } = event; const deviceInfo = event.deviceInfo || {}; let params = event.params || {}; /* 2.在某些操作之前我们要对用户对身份进行校验(也就是要检查用户的token)再将得到的uid写入params.uid 校验用到的方法是uniID.checkToken 详情:https://uniapp.dcloud.io/uniCloud/uni-id?id=checktoken 讨论,我们假设一个这样的场景,代码如下。 如: uniCloud.callFunction({ name:"xxx", data:{ "params":{ uid:"通过某种方式获取来的别人的uid" } } }) 用户就这样轻易地伪造了他人的uid传递给服务端,有一句话叫:前端从来的数据是不可信任的 所以这里我们需要将uniID.checkToken返回的uid写入到params.uid */ let noCheckAction = ['register', 'checkToken', 'login', 'logout', 'sendSmsCode', 'createCaptcha', 'verifyCaptcha', 'refreshCaptcha', 'inviteLogin', 'loginByWeixin', 'loginByUniverify', 'loginByApple', 'loginBySms', 'resetPwdBySmsCode', 'registerAdmin' ] if (!noCheckAction.includes(action)) { if (!uniIdToken) { return { code: 403, msg: '缺少token' } } let payload = await uniID.checkToken(uniIdToken) if (payload.code && payload.code > 0) { return payload } params.uid = payload.uid } //禁止前台用户传递角色 if (action.slice(0, 7) == "loginBy") { if (params.role) { return { code: 403, msg: '禁止前台用户传递角色' } } } //3.注册成功后创建新用户的积分表方法 async function registerSuccess(uid) { //用户接受邀请 if (inviteCode) { await uniID.acceptInvite({ inviteCode, uid }); } //添加当前用户设备信息 await db.collection('uni-id-device').add({ ...deviceInfo, user_id: uid }) await db.collection('uni-id-scores').add({ user_id: uid, score: 1, type: 1, balance: 1, comment: "", create_date: Date.now() }) } //4.记录成功登录的日志方法 const loginLog = async (res = {}) => { const now = Date.now() const uniIdLogCollection = db.collection('uni-id-log') let logData = { deviceId: params.deviceId || context.DEVICEID, ip: params.ip || context.CLIENTIP, type: res.type, ua: context.CLIENTUA, create_date: now }; if(res.code === 0){ logData.user_id = res.uid logData.state = 1 if(res.userInfo&&res.userInfo.password){ delete res.userInfo.password } if (res.type == 'register') { await registerSuccess(res.uid) } else { if (Object.keys(deviceInfo).length) { // console.log(979797, { // deviceInfo, // user_id: res // }); //更新当前用户设备信息 await db.collection('uni-id-device').where({ user_id: res.uid }).update(deviceInfo) } } }else{ logData.state = 0 } return await uniIdLogCollection.add(logData) } let res = {} switch (action) { //根据action的值执行对应的操作 case 'bindMobileByUniverify': let { appid, apiKey, apiSecret } = uniIdConfig.service.univerify let univerifyRes = await uniCloud.getPhoneNumber({ provider: 'univerify', appid, apiKey, apiSecret, access_token: params.access_token, openid: params.openid }) if (univerifyRes.code === 0) { res = await uniID.bindMobile({ uid: params.uid, mobile: univerifyRes.phoneNumber }) res.mobile = univerifyRes.phoneNumber } break; case 'bindMobileBySms': // console.log({ // uid: params.uid, // mobile: params.mobile, // code: params.code // }); res = await uniID.bindMobile({ uid: params.uid, mobile: params.mobile, code: params.code }) // console.log(res); break; case 'register': var { username, password, nickname } = params if (/^1\d{10}$/.test(username)) { return { code: 401, msg: '用户名不能是手机号' } }; if (/^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/.test(username)) { return { code: 401, msg: '用户名不能是邮箱' } } res = await uniID.register({ username, password, nickname, inviteCode }); if (res.code === 0) { await registerSuccess(res.uid) } break; case 'login': //防止黑客恶意破解登录,连续登录失败一定次数后,需要用户提供验证码 const getNeedCaptcha = async () => { //当用户最近“2小时内(recordDate)”登录失败达到2次(recordSize)时。要求用户提交验证码 const now = Date.now(), recordDate = 120 * 60 * 1000, recordSize = 2; const uniIdLogCollection = db.collection('uni-id-log') let recentRecord = await uniIdLogCollection.where({ deviceId: params.deviceId || context.DEVICEID, create_date: dbCmd.gt(now - recordDate), type: 'login' }) .orderBy('create_date', 'desc') .limit(recordSize) .get(); return recentRecord.data.filter(item => item.state === 0).length === recordSize; } let passed = false; let needCaptcha = await getNeedCaptcha(); console.log('needCaptcha', needCaptcha); if (needCaptcha) { res = await uniCaptcha.verify({ ...params, scene: 'login' }) if (res.code === 0) passed = true; } if (!needCaptcha || passed) { res = await uniID.login({ ...params, queryField: ['username', 'email', 'mobile'] }); res.type = 'login' await loginLog(res); needCaptcha = await getNeedCaptcha(); } res.needCaptcha = needCaptcha; break; case 'loginByWeixin': let loginRes = await uniID.loginByWeixin(params); if(loginRes.code===0){ //用户完善资料(昵称、头像) if(context.PLATFORM == "app-plus" && !loginRes.userInfo.nickname){ let {accessToken:access_token,openid} = loginRes, {appid,appsecret:secret} = uniIdConfig['app-plus'].oauth.weixin; let wxRes = await uniCloud.httpclient.request( `https://api.weixin.qq.com/sns/userinfo?access_token=${access_token}&openid=${openid}&scope=snsapi_userinfo&appid=${appid}&secret=${secret}`, { method: 'POST', contentType: 'json', // 指定以application/json发送data内的数据 dataType: 'json' // 指定返回值为json格式,自动进行parse }) if(wxRes.status == 200){ let {nickname,headimgurl} = wxRes.data; let headimgurlFile = {},cloudPath = loginRes.uid+'/'+Date.now()+"headimgurl.jpg"; let getImgBuffer = await uniCloud.httpclient.request(headimgurl) if(getImgBuffer.status == 200){ let {fileID} = await uniCloud.uploadFile({ cloudPath, fileContent: getImgBuffer.data }); headimgurlFile = { name:cloudPath, extname:"jpg", url:fileID } }else{ return getImgBuffer } await uniID.updateUser({ uid: loginRes.uid, nickname, avatar_file:headimgurlFile }) loginRes.userInfo.nickname = nickname; loginRes.userInfo.avatar_file = headimgurlFile; }else{ return wxRes } } delete loginRes.accessToken delete loginRes.refreshToken return loginRes }else{ return loginRes } await loginLog(res) break; case 'loginByUniverify': res = await uniID.loginByUniverify(params) await loginLog(res) break; case 'loginByApple': res = await uniID.loginByApple(params) await loginLog(res) break; case 'checkToken': res = await uniID.checkToken(uniIdToken); break; case 'logout': res = await uniID.logout(uniIdToken) break; case 'sendSmsCode': /* -开始- 测试期间,为节约资源。统一虚拟短信验证码为: 123456;开启以下代码块即可 */ // return uniID.setVerifyCode({ // mobile: params.mobile, // code: '123456', // type: params.type // }) /* -结束- */ // 简单限制一下客户端调用频率 const ipLimit = await db.collection('opendb-verify-codes').where({ ip: context.CLIENTIP, created_at: dbCmd.gt(Date.now() - 60000) }).get() if (ipLimit.data.length > 0) { return { code: 429, msg: '请求过于频繁' } } const templateId = '11753' // 替换为自己申请的模板id if (!templateId) { return { code: 500, msg: 'sendSmsCode需要传入自己的templateId,参考https://uniapp.dcloud.net.cn/uniCloud/uni-id?id=sendsmscode' } } const randomStr = '00000' + Math.floor(Math.random() * 1000000) const code = randomStr.substring(randomStr.length - 6) res = await uniID.sendSmsCode({ mobile: params.mobile, code, type: params.type, templateId }) break; case 'loginBySms': if (!params.code) { return { code: 500, msg: '请填写验证码' } } if (!/^1\d{10}$/.test(params.mobile)) { return { code: 500, msg: '手机号码填写错误' } } res = await uniID.loginBySms(params) await loginLog(res) break; case 'resetPwdBySmsCode': if (!params.code) { return { code: 500, msg: '请填写验证码' } } if (!/^1\d{10}$/.test(params.mobile)) { return { code: 500, msg: '手机号码填写错误' } } params.type = 'login' let loginBySmsRes = await uniID.loginBySms(params) // console.log(loginBySmsRes); if (loginBySmsRes.code === 0) { res = await uniID.resetPwd({ password: params.password, "uid": loginBySmsRes.uid }) } else { return loginBySmsRes } break; case 'getInviteCode': res = await uniID.getUserInfo({ uid: params.uid, field: ['my_invite_code'] }) if (res.code === 0) { res.myInviteCode = res.userInfo.my_invite_code delete res.userInfo } break; case 'getInvitedUser': res = await uniID.getInvitedUser(params) break; case 'updatePwd': res = await uniID.updatePwd(params) break; case 'createCaptcha': res = await uniCaptcha.create(params) break; case 'refreshCaptcha': res = await uniCaptcha.refresh(params) break; case 'getUserInviteCode': res = await uniID.getUserInfo({ uid: params.uid, field: ['my_invite_code'] }) if (!res.userInfo.my_invite_code) { res = await uniID.setUserInviteCode({ uid: params.uid }) } break; // =========================== admin api start ========================= case 'registerAdmin': { var { username, password } = params let { total } = await db.collection('uni-id-users').where({ role: 'admin' }).count() if (total) { return { code: 10001, message: '超级管理员已存在,请登录...' } } const appid = params.appid const appName = params.appName delete params.appid delete params.appName res = await uniID.register({ username, password, role: ["admin"] }) if (res.code === 0) { const app = await db.collection('opendb-app-list').where({ appid }).count() if (!app.total) { await db.collection('opendb-app-list').add({ appid, name: appName, description: "admin 管理后台", create_date: Date.now() }) } } } break; case 'registerUser': const { userInfo } = await uniID.getUserInfo({ uid: params.uid }) if (userInfo.role.indexOf('admin') === -1) { res = { code: 403, message: '非法访问, 无权限注册超级管理员', } } else { // 过滤 dcloud_appid,注册用户成功后再提交 const dcloudAppidList = params.dcloud_appid delete params.dcloud_appid res = await uniID.register({ autoSetDcloudAppid: false, ...params }) if (res.code === 0) { delete res.token delete res.tokenExpired await uniID.setAuthorizedAppLogin({ uid: res.uid, dcloudAppidList }) } } break; case 'updateUser': { const { userInfo } = await uniID.getUserInfo({ uid: params.uid }) if (userInfo.role.indexOf('admin') === -1) { res = { code: 403, message: '非法访问, 无权限注册超级管理员', } } else { // 过滤 dcloud_appid,注册用户成功后再提交 const dcloudAppidList = params.dcloud_appid delete params.dcloud_appid // 过滤 password,注册用户成功后再提交 const password = params.password delete params.password // 过滤 uid、id const id = params.id delete params.id delete params.uid res = await uniID.updateUser({ uid: id, ...params }) if (res.code === 0) { if (password) { await uniID.resetPwd({ uid: id, password }) } await uniID.setAuthorizedAppLogin({ uid: id, dcloudAppidList }) } } break; } case 'getCurrentUserInfo': res = await uniID.getUserInfo({ uid: params.uid, ...params }) break; // =========================== admin api end ========================= default: res = { code: 403, msg: '非法访问' } break; } //返回数据给客户端 return res }