服务端代码:
说明
const uniIdCommon = require('uni-id-common') const uniCaptcha = require('uni-captcha') const {getType,checkIdCard} = require('./common/utils') const {checkClientInfo,Validator} = require('./common/validator') const ConfigUtils = require('./lib/utils/config') const {isUniIdError,ERROR} = require('./common/error') const middleware = require('./middleware/index') const universal = require('./common/universal') const {registerAdmin,registerUser,registerUserByEmail} = require('./module/register/index') const {addUser,updateUser} = require('./module/admin/index') const {login,loginBySms,loginByUniverify,loginByWeixin,loginByAlipay,loginByQQ,loginByApple,loginByWeixinMobile} = require('./module/login/index') const {logout} = require('./module/logout/index') const {bindMobileBySms,bindMobileByUniverify,bindMobileByMpWeixin,bindAlipay,bindApple,bindQQ,bindWeixin, unbindWeixin,unbindAlipay,unbindQQ,unbindApple} = require('./module/relate/index') const {setPwd,updatePwd,resetPwdBySms,resetPwdByEmail,closeAccount,getAccountInfo,getRealNameInfo} = require('./module/account/index') const {createCaptcha,refreshCaptcha,sendSmsCode,sendEmailCode} = require('./module/verify/index') const {refreshToken,setPushCid,secureNetworkHandshakeByWeixin} = require('./module/utils/index') const {getInvitedUser,acceptInvite} = require('./module/fission') const {authorizeAppLogin,removeAuthorizedApp,setAuthorizedApp} = require('./module/multi-end') const {getSupportedLoginType} = require('./module/dev/index') const {externalRegister,externalLogin,updateUserInfoByExternal} = require('./module/external') const {getFrvCertifyId,getFrvAuthResult} = require('./module/facial-recognition-verify') module.exports = { async _before () { // 支持 callFunction 与 URL化 universal.call(this) const clientInfo = this.getUniversalClientInfo() /** * 检查clientInfo,无appId和uniPlatform时本云对象无法正常运行 * 此外需要保证用到的clientInfo字段均经过类型检查 * clientInfo由客户端上传并非完全可信,clientInfo内除clientIP、userAgent、source外均为客户端上传参数 * 否则可能会出现一些意料外的情况 */ checkClientInfo(clientInfo) let clientPlatform = clientInfo.uniPlatform // 统一platform名称 switch (clientPlatform) { case 'app': case 'app-plus': clientPlatform = 'app' break case 'web': case 'h5': clientPlatform = 'web' break default: break } this.clientPlatform = clientPlatform // 挂载uni-id实例到this上,方便后续调用 this.uniIdCommon = uniIdCommon.createInstance({ clientInfo }) // 包含uni-id配置合并等功能的工具集 this.configUtils = new ConfigUtils({ context: this }) this.config = this.configUtils.getPlatformConfig() this.hooks = this.configUtils.getHooks() this.validator = new Validator({ passwordStrength: this.config.passwordStrength }) // 扩展 validator 增加 验证身份证号码合法性 this.validator.mixin('idCard', function (idCard) { if (!checkIdCard(idCard)) { return { errCode: ERROR.INVALID_ID_CARD } } }) this.validator.mixin('realName', function (realName) { if ( typeof realName !== 'string' || realName.length < 2 || !/^[\u4e00-\u9fa5]{1,10}(·?[\u4e00-\u9fa5]{1,10}){0,5}$/.test(realName) ) { return { errCode: ERROR.INVALID_REAL_NAME } } }) // 挂载uni-captcha到this上,方便后续调用 this.uniCaptcha = uniCaptcha Object.defineProperty(this, 'uniOpenBridge', { get () { return require('uni-open-bridge-common') } }) // 挂载中间件 this.middleware = {} for (const mwName in middleware) { this.middleware[mwName] = middleware[mwName].bind(this) } // 国际化 const messages = require('./lang/index') const fallbackLocale = 'zh-Hans' const i18n = uniCloud.initI18n({ locale: clientInfo.locale, fallbackLocale, messages: JSON.parse(JSON.stringify(messages)) }) if (!messages[i18n.locale]) { i18n.setLocale(fallbackLocale) } this.t = i18n.t.bind(i18n) this.response = {} // 请求鉴权验证 await this.middleware.verifyRequestSign() // 通用权限校验模块 await this.middleware.accessControl() }, _after (error, result) { if (error) { // 处理中间件内抛出的标准响应对象 if (error.errCode && getType(error) === 'object') { const errCode = error.errCode if (!isUniIdError(errCode)) { return error } return { errCode, errMsg: error.errMsg || this.t(errCode, error.errMsgValue) } } throw error } return Object.assign(this.response, result) }, /** * 注册管理员 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#register-admin * @param {Object} params * @param {String} params.username 用户名 * @param {String} params.password 密码 * @param {String} params.nickname 昵称 * @returns */ registerAdmin, ...
end
1、registerAdmin-注册管理员
A、服务端代码
/module/register/index.js
module.exports = {
registerUser: require('./register-user'),
registerAdmin: require('./register-admin'),
registerUserByEmail: require('./register-user-by-email')
}
register-admin
const {userCollection} = require('../../common/constants')
const {ERROR} = require('../../common/error')
const {preRegisterWithPassword,postRegister} = require('../../lib/utils/register')
/**
* 注册管理员
* @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#register-admin
* @param {Object} params
* @param {String} params.username 用户名
* @param {String} params.password 密码
* @param {String} params.nickname 昵称
* @returns
*/
module.exports = async function (params = {}) {
//超级用户数据结构
const schema = {
username: 'username',
password: 'password',
nickname: {type: 'nickname',required: false}
}
//字段验证
this.middleware.validate(params, schema)
//取出参数
const {username,password,nickname} = params
//取一个管理员
const getAdminRes = await userCollection.where({role: 'admin'}).limit(1).get()
if (getAdminRes.data.length > 0) {//已经有数据了
const [admin] = getAdminRes.data //get出的是数组,admin是对象
const appId = this.getUniversalClientInfo().appId //这是什么东东?
if (!admin.dcloud_appid || (admin.dcloud_appid && admin.dcloud_appid.includes(appId))) {
return {errCode: ERROR.ADMIN_EXISTS,errMsg: this.t('uni-id-admin-exists')} //多语言返回,这个this.t是如何加上的?
} else {
return {errCode: ERROR.ADMIN_EXISTS,errMsg: this.t('uni-id-admin-exist-in-other-apps')}
}
}
const {user,extraData} = await preRegisterWithPassword.call(this, {user: {username},password}) //????
return postRegister.call(this, {user,extraData: {
...extraData,nickname,role: ['admin']}
})
}
preRegisterWithPassword.call:
/uni-id-co/lib/utils/register.js
const {userCollection,LOG_TYPE} = require('../../common/constants')
const {ERROR} = require('../../common/error')
const {findUser} = require('./account')
const {getValidInviteCode,generateInviteInfo} = require('./fission')
const {logout} = require('./logout')
const PasswordUtils = require('./password')
const merge = require('lodash.merge')
async function realPreRegister (params = {}) {
const {user} = params
const {userMatched} = await findUser({userQuery: user,authorizedApp: this.getUniversalClientInfo().appId})
if (userMatched.length > 0) {
throw {errCode: ERROR.ACCOUNT_EXISTS}
}
}
async function preRegister (params = {}) {
try {
await realPreRegister.call(this, params)
} catch (error) {
await this.middleware.uniIdLog({success: false,type: LOG_TYPE.REGISTER})
throw error
}
}
async function preRegisterWithPassword (params = {}) {
const {user,password} = params
await preRegister.call(this, {user})
const passwordUtils = new PasswordUtils({clientInfo: this.getUniversalClientInfo(),passwordSecret: this.config.passwordSecret})
const {passwordHash,version} = passwordUtils.generatePasswordHash({password})
const extraData = {password: passwordHash,password_secret_version: version}
return {user,extraData}
}
async function thirdPartyRegister ({user = {}} = {}) {
return {
mobileConfirmed: !!(user.mobile && user.mobile_confirmed) || false,
emailConfirmed: !!(user.email && user.email_confirmed) || false
}
}
async function postRegister (params = {}) {
const {user,extraData = {},isThirdParty = false,inviteCode} = params
const {appId,appName,appVersion,appVersionCode,channel,scene,clientIP,osName} = this.getUniversalClientInfo()
const uniIdToken = this.getUniversalUniIdToken()
merge(user, extraData)
const registerChannel = channel || scene
user.register_env = {
appid: appId || '',
uni_platform: this.clientPlatform || '',
os_name: osName || '',
app_name: appName || '',
app_version: appVersion || '',
app_version_code: appVersionCode || '',
channel: registerChannel ? registerChannel + '' : '', // channel可能为数字,统一存为字符串
client_ip: clientIP || ''
}
user.register_date = Date.now()
user.dcloud_appid = [appId]
if (user.username) {user.username = user.username.toLowerCase()}
if (user.email) {user.email = user.email.toLowerCase()}
const {
autoSetInviteCode, // 注册时自动设置邀请码
forceInviteCode, // 必须有邀请码才允许注册,注意此逻辑不可对admin生效
userRegisterDefaultRole // 用户注册时配置的默认角色
} = this.config
if (autoSetInviteCode) {
user.my_invite_code = await getValidInviteCode()
}
// 如果用户注册默认角色配置存在且不为空数组
if (userRegisterDefaultRole && userRegisterDefaultRole.length) {
// 将用户已有的角色和配置的默认角色合并成一个数组,并去重
user.role = Array.from(new Set([...(user.role || []), ...userRegisterDefaultRole]))
}
const isAdmin = user.role && user.role.includes('admin')
if (forceInviteCode && !isAdmin && !inviteCode) {
throw {
errCode: ERROR.INVALID_INVITE_CODE
}
}
if (inviteCode) {
const {inviterUid,inviteTime} = await generateInviteInfo({inviteCode})
user.inviter_uid = inviterUid
user.invite_time = inviteTime
}
if (uniIdToken) {
try {
await logout.call(this)
} catch (error) { }
}
const beforeRegister = this.hooks.beforeRegister
let userRecord = user
if (beforeRegister) {
userRecord = await beforeRegister({userRecord,clientInfo: this.getUniversalClientInfo()})
}
const {id: uid} = await userCollection.add(userRecord)
const createTokenRes = await this.uniIdCommon.createToken({uid})
const {errCode,token,tokenExpired} = createTokenRes
if (errCode) {throw createTokenRes}
await this.middleware.uniIdLog({data: {user_id: uid},type: LOG_TYPE.REGISTER})
return { errCode: 0,uid,newToken: {token,tokenExpired},
...(isThirdParty ? thirdPartyRegister({user: {...userRecord,_id: uid}}) : {} ),
passwordConfirmed: !!userRecord.password
}
}
module.exports = {
preRegister,
preRegisterWithPassword,
postRegister
}
B、问题
this.getUniversalClientInfo(),不知何义?
this.t如何引入?
call是什么意思?
C、客户端调用
await uniIdCo.registerAdmin({
username,
password,
nickname
})
参数说明
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
username | string | 是 | 超级管理员用户名 |
password | string | 是 | 超级管理员密码 |
nickname | string | 否 | 超级管理员昵称 |
返回值
参数名 | 类型 | 说明 |
---|---|---|
errCode | string | 错误码 |
errMsg | string | 错误信息 |
newToken | object | token信息 |
|-token | string | token |
|-tokenExpired | string | token过期时间 |
注意:
系统中仅可存在一个超级管理员
D、实例
/uni_modules/uni-id-pages/register/register-admin.vue
...
const uniIdCo = uniCloud.importObject("uni-id-co", {customUI: true})
export default {
mixins: [mixin],
data() {
return {
formData: {
username: "",
nickname: "",
password: "",
password2: "",
captcha: ""
},
...
submitForm(params) {
uniIdCo.registerAdmin(this.formData).then(e => {
uni.navigateBack()
})
.catch(e => {
//更好的体验:登录错误,直接刷新验证码
this.$refs.captcha.getImageCaptcha()
uni.showModal({
title: '提示',
content: e.errMsg || `创建失败: ${e.errCode}`,
showCancel: false
})
})
},
2、addUser-管理接口
A、服务器代码
/uni-id-co/module/admin/add-user.js
const {findUser} = require('../../lib/utils/account') const {ERROR} = require('../../common/error') const {userCollection} = require('../../common/constants') const PasswordUtils = require('../../lib/utils/password') /** * 新增用户 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#add-user * @param {Object} params * @param {String} params.username 用户名 * @param {String} params.password 密码 * @param {String} params.nickname 昵称 * @param {Array} params.authorizedApp 允许登录的AppID列表 * @param {Array} params.role 用户角色列表 * @param {String} params.mobile 手机号 * @param {String} params.email 邮箱 * @param {Array} params.tags 用户标签 * @param {Number} params.status 用户状态 * @returns */ module.exports = async function (params = {}) { const schema = { username: 'username', password: 'password', authorizedApp: {required: false,type: 'array<string>'}, // 指定允许登录的app,传空数组或不传时表示可以不可以在任何端登录 nickname: {required: false,type: 'nickname'}, role: {required: false,type: 'array<string>'}, mobile: {required: false,type: 'mobile'}, email: {required: false,type: 'email'}, tags: {required: false,type: 'array<string>'}, status: {required: false,type: 'number'} } this.middleware.validate(params, schema) const {username,password,authorizedApp,nickname,role,mobile,email,tags,status} = params const {userMatched} = await findUser({ userQuery: {username,mobile,email},authorizedApp }) if (userMatched.length) { throw {errCode: ERROR.ACCOUNT_EXISTS} } const passwordUtils = new PasswordUtils({ clientInfo: this.getUniversalClientInfo(), passwordSecret: this.config.passwordSecret }) const {passwordHash,version} = passwordUtils.generatePasswordHash({password}) const data = {username,password: passwordHash,password_secret_version: version, dcloud_appid: authorizedApp || [],nickname, role: role || [],mobile,email, tags: tags || [],status } if (email) {data.email_confirmed = 1} if (mobile) {data.mobile_confirmed = 1} // 触发 beforeRegister 钩子 const beforeRegister = this.hooks.beforeRegister let userRecord = data if (beforeRegister) { userRecord = await beforeRegister({ userRecord,clientInfo: this.getUniversalClientInfo() }) } await userCollection.add(userRecord) return {errCode: 0,errMsg: ''} }
B、问题
- this.middleware.validate 是不是中间层验证?
- this.hooks.beforeRegister 这个钩子是什么?
C、客户端
await uniIdCo.addUser({
username,
password,
authorizedApp,
nickname,
role
})
参数说明
参数名类型必填说明usernamestring是用户名passwordstring是密码authorizedAppArray <string>
否允许登录nicknamestring否昵称roleArray <string>
否用户角色mobilestring否手机号emailstring否邮箱tagsarray否用户标签statusnumber否用户状态,0:正常状态,1:封禁状态
2:审核中,3:审核失败,4:注销状态
返回值
参数名 | 类型 | 说明 |
---|---|---|
errCode | string | 错误码 |
errMsg | string | 错误信息 |
没用调用过未知
3、updateUser-修改用户
A、服务端代码
module.exports = {
addUser: require('./add-user'),
updateUser: require('./update-user')
}
/module/admin/update-user.js
const {findUser} = require('../../lib/utils/account')
const {ERROR} = require('../../common/error')
const {userCollection} = require('../../common/constants')
const PasswordUtils = require('../../lib/utils/password')
/**
* 修改用户
* @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#update-user
* @param {Object} params
* @param {String} params.uid 要更新的用户id
* @param {String} params.username 用户名
* @param {String} params.password 密码
* @param {String} params.nickname 昵称
* @param {Array} params.authorizedApp 允许登录的AppID列表
* @param {Array} params.role 用户角色列表
* @param {String} params.mobile 手机号
* @param {String} params.email 邮箱
* @param {Array} params.tags 用户标签
* @param {Number} params.status 用户状态
* @returns
*/
module.exports = async function (params = {}) {
const schema = {
uid: 'string',
username: 'username',
password: {required: false,type: 'password'},
authorizedApp: {required: false,type: 'array<string>'}, // 指定允许登录的app,传空数组或不传时表示可以不可以在任何端登录
nickname: {required: false,type: 'nickname'},
role: {required: false,type: 'array<string>'},
mobile: {required: false,type: 'mobile'},
email: {required: false,type: 'email'},
tags: {required: false,type: 'array<string>'},
status: {required: false,type: 'number'}
}
this.middleware.validate(params, schema)
const {uid,username,password,authorizedApp,nickname,role,mobile,email,tags,status} = params
// 更新的用户数据字段
const data = {username,dcloud_appid: authorizedApp,nickname,role,mobile,email,tags,status}
const realData = Object.keys(data).reduce((res, key) => {
const item = data[key]
if (item !== undefined) {res[key] = item}
return res
}, {})
// 更新用户名时验证用户名是否重新
if (username) {
const {userMatched} = await findUser({userQuery: {username},authorizedApp})
if (userMatched.filter(user => user._id !== uid).length) {
throw {errCode: ERROR.ACCOUNT_EXISTS}
}
}
if (password) {
const passwordUtils = new PasswordUtils({
clientInfo: this.getUniversalClientInfo(),
passwordSecret: this.config.passwordSecret
})
const {passwordHash,version} = passwordUtils.generatePasswordHash({password})
realData.password = passwordHash
realData.password_secret_version = version
}
await userCollection.doc(uid).update(realData)
return {errCode: 0}
}
B、问题
这是个什么?
C、客户端
// 云函数代码
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
}
更新用户信息
参数说明
字段 | 类型 | 必填 | 说明 | |
---|---|---|---|---|
uid | String | 是 | 用户Id,可以通过checkToken返回 | |
其余参数 | Any | 是 | 要设置的用户信息 |
响应参数
字段 | 类型 | 必填 | 说明 |
---|---|---|---|
code | Number | 是 | 错误码,0表示成功 |
message | String | 是 | 详细信息 |
4、authorizeAppLogin-授权用户登录指定客户端
A、服务端代码
/uni-id-co/module/multi-end/index.js
module.exports = {
authorizeAppLogin: require('./authorize-app-login'),
removeAuthorizedApp: require('./remove-authorized-app'),
setAuthorizedApp: require('./set-authorized-app')
}
authorize-app-login.js
const {isAuthorizeApproved} = require('./utils')
const {dbCmd,userCollection} = require('../../common/constants')
/**
* 授权用户登录应用
* @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#authorize-app-login
* @param {Object} params
* @param {String} params.uid 用户id
* @param {String} params.appId 授权的应用的AppId
* @returns
*/
module.exports = async function (params = {}) {
const schema = {uid: 'string',appId: 'string'}
this.middleware.validate(params, schema)
const {uid,appId} = params
await isAuthorizeApproved({uid,appIdList: [appId]})
await userCollection.doc(uid).update({dcloud_appid: dbCmd.push(appId)})
return {errCode: 0}
}
utils
const {userCollection} = require('../../common/constants')
const {ERROR} = require('../../common/error')
const {findUser} = require('../../lib/utils/account')
async function isAuthorizeApproved ({uid,appIdList} = {}) {
const getUserRes = await userCollection.doc(uid).get()
const userRecord = getUserRes.data[0]
if (!userRecord) {
throw {errCode: ERROR.ACCOUNT_NOT_EXISTS}
}
const {userMatched} = await findUser({userQuery: userRecord,authorizedApp: appIdList})
if (userMatched.some(item => item._id !== uid)) {
throw {errCode: ERROR.ACCOUNT_CONFLICT}
}
}
module.exports = {
isAuthorizeApproved
}
/common/constants.js
const db = uniCloud.database()
const dbCmd = db.command
const userCollectionName = 'uni-id-users'
const userCollection = db.collection(userCollectionName)
const verifyCollectionName = 'opendb-verify-codes'
const verifyCollection = db.collection(verifyCollectionName)
const deviceCollectionName = 'uni-id-device'
const deviceCollection = db.collection(deviceCollectionName)
const openDataCollectionName = 'opendb-open-data'
const openDataCollection = db.collection(openDataCollectionName)
const frvLogsCollectionName = 'opendb-frv-logs'
const frvLogsCollection = db.collection(frvLogsCollectionName)
const USER_IDENTIFIER = {
_id: 'uid',
username: 'username',
mobile: 'mobile',
email: 'email',
wx_unionid: 'wechat-account',
'wx_openid.app': 'wechat-account',
'wx_openid.mp': 'wechat-account',
'wx_openid.h5': 'wechat-account',
'wx_openid.web': 'wechat-account',
qq_unionid: 'qq-account',
'qq_openid.app': 'qq-account',
'qq_openid.mp': 'qq-account',
ali_openid: 'alipay-account',
apple_openid: 'alipay-account',
identities: 'idp'
}
const USER_STATUS = {
NORMAL: 0,
BANNED: 1,
AUDITING: 2,
AUDIT_FAILED: 3,
CLOSED: 4
}
const CAPTCHA_SCENE = {
REGISTER: 'register',
LOGIN_BY_PWD: 'login-by-pwd',
LOGIN_BY_SMS: 'login-by-sms',
RESET_PWD_BY_SMS: 'reset-pwd-by-sms',
RESET_PWD_BY_EMAIL: 'reset-pwd-by-email',
SEND_SMS_CODE: 'send-sms-code',
SEND_EMAIL_CODE: 'send-email-code',
BIND_MOBILE_BY_SMS: 'bind-mobile-by-sms',
SET_PWD_BY_SMS: 'set-pwd-by-sms'
}
const LOG_TYPE = {
LOGOUT: 'logout',
LOGIN: 'login',
REGISTER: 'register',
RESET_PWD_BY_SMS: 'reset-pwd',
RESET_PWD_BY_EMAIL: 'reset-pwd',
BIND_MOBILE: 'bind-mobile',
BIND_WEIXIN: 'bind-weixin',
BIND_QQ: 'bind-qq',
BIND_APPLE: 'bind-apple',
BIND_ALIPAY: 'bind-alipay',
UNBIND_WEIXIN: 'unbind-weixin',
UNBIND_QQ: 'unbind-qq',
UNBIND_ALIPAY: 'unbind-alipay',
UNBIND_APPLE: 'unbind-apple'
}
const SMS_SCENE = {
LOGIN_BY_SMS: 'login-by-sms',
RESET_PWD_BY_SMS: 'reset-pwd-by-sms',
BIND_MOBILE_BY_SMS: 'bind-mobile-by-sms',
SET_PWD_BY_SMS: 'set-pwd-by-sms'
}
const EMAIL_SCENE = {
REGISTER: 'register',
LOGIN_BY_EMAIL: 'login-by-email',
RESET_PWD_BY_EMAIL: 'reset-pwd-by-email',
BIND_EMAIL: 'bind-email'
}
const REAL_NAME_STATUS = {
NOT_CERTIFIED: 0,
WAITING_CERTIFIED: 1,
CERTIFIED: 2,
CERTIFY_FAILED: 3
}
const EXTERNAL_DIRECT_CONNECT_PROVIDER = 'externalDirectConnect'
module.exports = {
db,
dbCmd,
userCollection,
verifyCollection,
deviceCollection,
openDataCollection,
frvLogsCollection,
USER_IDENTIFIER,
USER_STATUS,
CAPTCHA_SCENE,
LOG_TYPE,
SMS_SCENE,
EMAIL_SCENE,
REAL_NAME_STATUS,
EXTERNAL_DIRECT_CONNECT_PROVIDER
}
B、问题
C、客户端
授权用户登录指定客户端
await uniIdCo.authorizeAppLogin({
uid,
appId
})
参数说明
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
uid | string | 是 | 用户id |
appId | string | 是 | 允许登录应用的DCloud AppId |
返回值
参数名 | 类型 | 说明 |
---|---|---|
errCode | string | 错误码 |
errMsg | string | 错误信息 |
newToken | object | token信息 |
- token | string | token |
- tokenExpired | string | token过期时间 |
没有实例
5、removeAuthorizedApp-移除用户登录授权
A、服务器端代码
/module/multi-end/index.js
module.exports = {
authorizeAppLogin: require('./authorize-app-login'),
removeAuthorizedApp: require('./remove-authorized-app'),
setAuthorizedApp: require('./set-authorized-app')
}
remove-authorized-app.js
const {dbCmd,userCollection} = require('../../common/constants')
/**
* 移除用户登录授权
* @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#remove-authorized-app
* @param {Object} params
* @param {String} params.uid 用户id
* @param {String} params.appId 取消授权的应用的AppId
* @returns
*/
module.exports = async function (params = {}) {
const schema = {uid: 'string',appId: 'string'}
this.middleware.validate(params, schema)
const {uid,appId} = params
await userCollection.doc(uid).update({
dcloud_appid: dbCmd.pull(appId)
})
return {errCode: 0}
}
B、问题
C、客户端
接口形式:
await uniIdCo.removeAuthorizedApp({
uid,
appId
})
没有例子
注意
- 此接口为管理端接口
- 仅在用户token即将过期时返回新newToken
6、setAuthorizedApp-设置允许登录的应用列表
A、服务器代码
/module/multi-end/set-authorized-app.js
const {isAuthorizeApproved} = require('./utils')
const {userCollection} = require('../../common/constants')
/**
* 设置用户允许登录的应用列表
* @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#set-authorized-app
* @param {Object} params
* @param {String} params.uid 用户id
* @param {Array} params.appIdList 允许登录的应用AppId列表
* @returns
*/
module.exports = async function (params = {}) {
const schema = {uid: 'string',appIdList: 'array<string>'}
this.middleware.validate(params, schema)
const {uid,appIdList} = params
await isAuthorizeApproved({uid,appIdList})
await userCollection.doc(uid).update({dcloud_appid: appIdList})
return {errCode: 0}
}
B、问题
C、客户端
接口形式
await uniIdCo.setAuthorizedApp({
uid,
appIdList
})
参数说明
参数名 | 类型 | 必填 | 说明 | |
---|---|---|---|---|
uid | string | 是 | 用户id | |
appIdList | Array <String> | 是 | 允许登录的应用对应的DCloud AppId列表 |
没有demo
7、registerUser-用户名密码注册用户
A、服务器代码
吕
const {postRegister,preRegisterWithPassword} = require('../../lib/utils/register') const {verifyCaptcha} = require('../../lib/utils/captcha') const {CAPTCHA_SCENE} = require('../../common/constants') /** * 注册普通用户 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#register-user * @param {Object} params * @param {String} params.username 用户名 * @param {String} params.password 密码 * @param {String} params.captcha 图形验证码 * @param {String} params.nickname 昵称 * @param {String} params.inviteCode 邀请码 * @returns */ module.exports = async function (params = {}) { const schema = {username: 'username',password: 'password',captcha: 'string', nickname: {required: false,type: 'nickname'}, inviteCode: {required: false,type: 'string'} } this.middleware.validate(params, schema) const {username,password,nickname,captcha,inviteCode} = params await verifyCaptcha.call(this, {captcha,scene: CAPTCHA_SCENE.REGISTER}) const {user,extraData} = await preRegisterWithPassword.call(this, { user: {username},password}) return postRegister.call(this, {user,extraData: {...extraData,nickname},inviteCode}) }
吕
B、问题
C、客户端
await uniIdCo.registerUser({ username, password, captcha, nickname, inviteCode })
参数说明
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
username | string | 是 | 用户名 |
password | string | 是 | 密码 |
captcha | string | 是 | 图形验证码 |
nickname | string | 是 | 呢称 |
inviteCode | string | 是 | 邀请码 |
返回值
参数名 | 类型 | 说明 |
---|---|---|
errCode | string | 错误码 |
errMsg | string | 错误信息 |
newToken | object | token信息 |
–token | string | token |
–tokenExpired | string | token过期时间 |
8、registerUserByEmail
功能:通过邮箱+验证码注册用户
A、调用端
接口形式:
await uniIdCo.registerUserByEmail({
email,
password,
code,
nickname,
inviteCode
})
参数说明:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
string | 是 | 邮箱 | |
password | string | 是 | 密码 |
code | string | 是 | 邮箱验证码 |
nickname | string | 否 | 呢称 |
inviteCode | string | 否 | 邀请码 |
返回值:
参数名 | 类型 | 说明 | |
---|---|---|---|
errCode | string/number | 错误码 | |
errMsg | string | 错误信息 | |
newToken | object | token信息 | |
token | string | token | |
tokenExpired | string | token过期时间 |
实例(无)
B、服务端代码
/uni-id-co/module/register/register-user-by-email.js
const {postRegister,preRegisterWithPassword} = require('../../lib/utils/register') const {verifyCaptcha} = require('../../lib/utils/captcha') const {CAPTCHA_SCENE,EMAIL_SCENE,LOG_TYPE} = require('../../common/constants') const {verifyEmailCode} = require('../../lib/utils/verify-code') /** * 通过邮箱+验证码注册普通用户 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#register-user-by-email * @param {Object} params * @param {String} params.email 邮箱 * @param {String} params.password 密码 * @param {String} params.nickname 昵称 * @param {String} params.code 邮箱验证码 * @param {String} params.inviteCode 邀请码 * @returns */ module.exports = async function (params = {}) { const schema = {email: 'email',password: 'password',nickname: {required: false,type: 'nickname'}, code: 'string',inviteCode: {required: false,type: 'string'}} this.middleware.validate(params, schema) const {email,password,nickname,code,inviteCode} = params try { // 验证邮箱验证码,验证不通过时写入失败日志 await verifyEmailCode({email,code,scene: EMAIL_SCENE.REGISTER}) } catch (error) { await this.middleware.uniIdLog({data: {email},type: LOG_TYPE.REGISTER,success: false}) throw error } const {user,extraData} = await preRegisterWithPassword.call(this, {user: {email},password}) return postRegister.call(this, {user,extraData: {...extraData,nickname,email_confirmed: 1},inviteCode}, ) }
C、问题
暂无
9、login
功能:密码登录
A、调用端
接口形式:
await uniIdCo.login({
username,
password,
captcha
})
参数说明:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
username | string | 和mobile、email三选一 | 用户名 |
mobile | string | 和username、email三选一 | 手机号 |
string | 和username、mobile三选一 | 邮箱 | |
password | string | 是 | 密码 |
captcha | string | 否 | 图形验证码 |
返回值:
参数名 | 类型 | 说明 |
---|---|---|
同上 | ||
实例()
B、服务端代码
/uni-id-co/module/login/login.js
const {preLoginWithPassword,postLogin} = require('../../lib/utils/login') const {getNeedCaptcha,verifyCaptcha} = require('../../lib/utils/captcha') const {CAPTCHA_SCENE} = require('../../common/constants') const {ERROR} = require('../../common/error') /** * 用户名密码登录 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login * @param {Object} params * @param {String} params.username 用户名 * @param {String} params.mobile 手机号 * @param {String} params.email 邮箱 * @param {String} params.password 密码 * @param {String} params.captcha 图形验证码 * @returns */ module.exports = async function (params = {}) { const schema = { username: {required: false,type: 'username'}, mobile: {required: false,type: 'mobile'}, email: {required: false,type: 'email'}, password: 'password', captcha: {required: false,type: 'string'} } this.middleware.validate(params, schema) const {username,mobile,email,password,captcha} = params if (!username && !mobile && !email) { throw {errCode: ERROR.INVALID_USERNAME} } else if ( (username && email) || (username && mobile) || (email && mobile) ) { throw {errCode: ERROR.INVALID_PARAM} } const needCaptcha = await getNeedCaptcha.call(this, {username,mobile,email},) if (needCaptcha) { await verifyCaptcha.call(this, {captcha,scene: CAPTCHA_SCENE.LOGIN_BY_PWD}) } const {user,extraData} = await preLoginWithPassword.call(this, { user: {username,mobile,email},password}) return postLogin.call(this, {user,extraData}) }
getNeedCaptcha
const {ERROR} = require('../../common/error') async function getNeedCaptcha ({uid,username,mobile,email,type = 'login', limitDuration = 7200000, // 两小时 limitTimes = 3 // 记录次数 } = {}) { const db = uniCloud.database() const dbCmd = db.command // 当用户最近“2小时内(limitDuration)”登录失败达到3次(limitTimes)时。要求用户提交验证码 const now = Date.now() const uniIdLogCollection = db.collection('uni-id-log') const userIdentifier = {user_id: uid,username,mobile,email} let totalKey = 0; let deleteKey = 0 for (const key in userIdentifier) { totalKey++ if (!userIdentifier[key] || typeof userIdentifier[key] !== 'string') { deleteKey++ delete userIdentifier[key] } } if (deleteKey === totalKey) { throw new Error('System error') // 正常情况下不会进入此条件,但是考虑到后续会有其他开发者修改此云对象,在此处做一个判断 } const {data: recentRecord} = await uniIdLogCollection.where({ ip: this.getUniversalClientInfo().clientIP,...userIdentifier,type, create_date: dbCmd.gt(now - limitDuration) }) .orderBy('create_date', 'desc') .limit(limitTimes) .get() return recentRecord.length === limitTimes && recentRecord.every(item => item.state === 0) } async function verifyCaptcha (params = {}) { const {captcha,scene} = params if (!captcha) { throw {errCode: ERROR.CAPTCHA_REQUIRED} } const payload = await this.uniCaptcha.verify({deviceId: this.getUniversalClientInfo().deviceId,captcha,scene}) if (payload.errCode) { throw payload } } module.exports = { getNeedCaptcha, verifyCaptcha }
C、问题
暂无
10、loginBySms
功能:手机号已存在时登录,否则注册
A、调用端
接口形式:
await uniIdCo.loginBySms({
mobile,
code,
captcha,
inviteCode
})
参数说明:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
mobile | string | 是 | 手机号 |
code | string | 是 | 短信验证码 |
captcha | string | 否 | 图形验证码 |
inviteCode | string | 否 | 邀请码,仅注册时生效 |
两小时内登录失败3次的用户必填图形验证码,如果客户端没有使用uni-id-pages,可以参考uni-id-pages验证码登录页面的相关逻辑。
返回值:
参数名 | 类型 | 说明 | |
---|---|---|---|
errCode | string | 错误码 | |
errMsg | string | 错误信息 | |
newToken | object | token信息 | |
token | string | token | |
tokenExpired | string | token过期时间 |
实例()
B、服务端代码
地址:/uni-id-co/module/login/login-by-sms.js
const {getNeedCaptcha,verifyCaptcha} = require('../../lib/utils/captcha') const {verifyMobileCode} = require('../../lib/utils/verify-code') const {preUnifiedLogin,postUnifiedLogin} = require('../../lib/utils/unified-login') const {CAPTCHA_SCENE,SMS_SCENE,LOG_TYPE} = require('../../common/constants') /** * 短信验证码登录 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-sms * @param {Object} params * @param {String} params.mobile 手机号 * @param {String} params.code 短信验证码 * @param {String} params.captcha 图形验证码 * @param {String} params.inviteCode 邀请码 * @returns */ module.exports = async function (params = {}) { const schema ={ mobile: 'mobile',code: 'string', captcha: {required: false,type: 'string'}, inviteCode: {required: false,type: 'string'} } this.middleware.validate(params, schema) const {mobile,code,captcha,inviteCode} = params const needCaptcha = await getNeedCaptcha.call(this, {mobile}) if (needCaptcha) { await verifyCaptcha.call(this, {captcha,scene: CAPTCHA_SCENE.LOGIN_BY_SMS}) } try { await verifyMobileCode({mobile,code,scene: SMS_SCENE.LOGIN_BY_SMS}) } catch (error) { console.log(error, {mobile,code,type: SMS_SCENE.LOGIN_BY_SMS}) await this.middleware.uniIdLog({success: false,data: {mobile},type: LOG_TYPE.LOGIN}) throw error } const {type,user} = await preUnifiedLogin.call(this, {user: {mobile}}) return postUnifiedLogin.call(this, {user,extraData: {mobile_confirmed: 1},isThirdParty: false,type,inviteCode}, ) }
C、问题
暂无
11、loginByUniverify
功能:一键登录
A、调用端
接口形式:
await uniIdCo.loginByUniverify({
access_token,
openid,
inviteCode
})
参数说明:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
access_token | string | 是 | 一键登录客户端返回的access_token |
openid | string | 是 | 一键登录客户端返回的openid |
inviteCode | string | 否 | 邀请码,仅注册时生效 |
返回值:
参数名 | 类型 | 说明 | |
---|---|---|---|
errCode | string | 错误码 | |
errMsg | string | 错误信息 | |
newToken | object | token信息 | |
token | string | token | |
tokenExpired | string | token过期 |
实例()
B、服务端代码
/uni-id-co/module/login/login-by-univerify.js
const {getPhoneNumber} = require('../../lib/utils/univerify') const {preUnifiedLogin,postUnifiedLogin} = require('../../lib/utils/unified-login') const {LOG_TYPE} = require('../../common/constants') /** * App端一键登录 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-univerify * @param {Object} params * @param {String} params.access_token APP端一键登录返回的access_token * @param {String} params.openid APP端一键登录返回的openid * @param {String} params.inviteCode 邀请码 * @returns */ module.exports = async function (params = {}) { const schema = {access_token: 'string',openid: 'string', inviteCode: {required: false,type: 'string'} } this.middleware.validate(params, schema) const {access_token,openid,inviteCode} = params let mobile try { const phoneInfo = await getPhoneNumber.call(this, {access_token,openid}) mobile = phoneInfo.phoneNumber } catch (error) { await this.middleware.uniIdLog({success: false,type: LOG_TYPE.LOGIN}) throw error } const {user,type} = await preUnifiedLogin.call(this, {user: {mobile}}) return postUnifiedLogin.call(this, {user,extraData: {mobile_confirmed: 1},type,inviteCode}) }
C、问题
call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象,第二个参数差别就来了:
var name="小王",age=17;
var obj={
name:"小张",
objAge:this.age,
myFun:function(fm,t){
console.log(this.name+"年龄"+this.age,"来自"+fm+"去往"+t);
}
}
var db={name:"德玛",age:99}
obj.myFun.call(db,'成都','上海'); //德玛 年龄 99 来自 成都去往上海
obj.myFun.apply(db,['成都','上海']); //德玛 年龄 99 来自 成都去往上海
obj.myFun.bind(db,'成都','上海')(); //德玛 年龄 99 来自 成都去往上海
12、loginByWeixin
功能:微信账号已存在时登录,否则注册
A、调用端
接口形式:
await uniIdCo.loginByWeixin({
code,
inviteCode
})
参数说明:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
code | string | 是 | uni.login 接口返回的code参数 |
inviteCode | string | 否 | 邀请码,仅注册时生效 |
返回值:
参数名 | 类型 | 说明 | |
---|---|---|---|
errCode | string | 错误码 | |
errMsg | string | 错误信息 | |
newToken | object | token信息 | |
token | string | token | |
tokenExpired | string | token过期时间 |
实例()
B、服务端代码
/uni-id-co/module/login/login-by-weixin.js
const {initWeixin} = require('../../lib/third-party/index') const {ERROR} = require('../../common/error') const {preUnifiedLogin,postUnifiedLogin} = require('../../lib/utils/unified-login') const {generateWeixinCache,getWeixinPlatform, saveWeixinUserKey,saveSecureNetworkCache} = require('../../lib/utils/weixin') const {LOG_TYPE} = require('../../common/constants') const url = require('url') /** * 微信登录 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-weixin * @param {Object} params * @param {String} params.code 微信登录返回的code * @param {String} params.inviteCode 邀请码 * @returns */ module.exports = async function (params = {}) { const schema = {code: 'string',inviteCode: {type: 'string',required: false}} this.middleware.validate(params, schema) const {code,inviteCode,secureNetworkCache = false} = params const {appId} = this.getUniversalClientInfo() const weixinApi = initWeixin.call(this) const weixinPlatform = getWeixinPlatform.call(this) let apiName switch (weixinPlatform) { case 'mp': apiName = 'code2Session' break case 'app': case 'h5': case 'web': apiName = 'getOauthAccessToken' break default: throw new Error('Unsupported weixin platform') } let getWeixinAccountResult try { getWeixinAccountResult = await weixinApi[apiName](code) } catch (error) { console.error(error) await this.middleware.uniIdLog({success: false,type: LOG_TYPE.LOGIN}) throw {errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED} } const {openid,unionid, // 保存下面四个字段 sessionKey, // 微信小程序用户sessionKey accessToken, // App端微信用户accessToken refreshToken, // App端微信用户refreshToken expired: accessTokenExpired // App端微信用户accessToken过期时间 } = getWeixinAccountResult if (secureNetworkCache) { if (weixinPlatform !== 'mp') { throw new Error('Unsupported weixin platform, expect mp-weixin') } await saveSecureNetworkCache.call(this, {code,openid,unionid,sessionKey}) } const {type,user} = await preUnifiedLogin.call(this, { user: {wx_openid: {[weixinPlatform]: openid},wx_unionid: unionid} }) const extraData = { wx_openid: {[`${weixinPlatform}_${appId}`]: openid}, wx_unionid: unionid } if (type === 'register' && weixinPlatform !== 'mp') { const {nickname,avatar} = await weixinApi.getUserInfo({accessToken,openid}) if (avatar) { // eslint-disable-next-line n/no-deprecated-api const avatarPath = url.parse(avatar).pathname const extName = avatarPath.indexOf('.') > -1 ? url.parse(avatar).pathname.split('.').pop() : 'jpg' const cloudPath = `user/avatar/${openid.slice(-8) + Date.now()}-avatar.${extName}` const getAvatarRes = await uniCloud.httpclient.request(avatar) if (getAvatarRes.status >= 400) { throw {errCode: ERROR.GET_THIRD_PARTY_USER_INFO_FAILED} } const {fileID} = await uniCloud.uploadFile({cloudPath,fileContent: getAvatarRes.data}) extraData.avatar_file = {name: cloudPath,extname: extName,url: fileID} } extraData.nickname = nickname } await saveWeixinUserKey.call(this, {openid,sessionKey,accessToken,refreshToken,accessTokenExpired}) return postUnifiedLogin.call(this, {user, extraData: {...extraData, ...generateWeixinCache.call(this, {openid, sessionKey, // 微信小程序用户sessionKey accessToken, // App端微信用户accessToken refreshToken, // App端微信用户refreshToken accessTokenExpired // App端微信用户accessToken过期时间 }) }, isThirdParty: true,type,inviteCode }) }
C、问题
暂无
13、loginByAlipay
功能:支付宝账号已存在时登录,否则注册
A、调用端
接口形式:
await uniIdCo.loginByAlipay({
code,
inviteCode
})
参数说明:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
code | string | 是 | 支付宝小程序 uni.login 返回的code参数 |
inviteCode | string | 否 | 邀请码,仅注册时生效 |
返回值:
参数名 | 类型 | 说明 | |
---|---|---|---|
errCode | string | 错误码 | |
errMsg | string | 错误信息 | |
newToken | object | token信息 | |
token | string | token | |
tokenExpired | string | token过期时间 |
实例()
B、服务端代码
/uni-id-co/module/login/login-by-alipay.js
const {initAlipay} = require('../../lib/third-party/index') const {ERROR} = require('../../common/error') const {preUnifiedLogin,postUnifiedLogin} = require('../../lib/utils/unified-login') const {LOG_TYPE} = require('../../common/constants') /** * 支付宝登录 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-alipay * @param {Object} params * @param {String} params.code 支付宝小程序客户端登录返回的code * @param {String} params.inviteCode 邀请码 * @returns */ module.exports = async function (params = {}) { const schema = {code: 'string',inviteCode: {type: 'string',required: false}} this.middleware.validate(params, schema) const {code,inviteCode} = params const alipayApi = initAlipay.call(this) let getAlipayAccountResult try { getAlipayAccountResult = await alipayApi.code2Session(code) } catch (error) { console.error(error) await this.middleware.uniIdLog({success: false,type: LOG_TYPE.LOGIN}) throw {errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED} } const {openid} = getAlipayAccountResult const {type,user} = await preUnifiedLogin.call(this, {user: {ali_openid: openid}}) return postUnifiedLogin.call(this, {user,extraData: {},isThirdParty: true,type,inviteCode}) }
C、问题
暂无
14、loginByQQ
功能:QQ账号已存在时登录,否则注册
A、调用端
接口形式:
await uniIdCo.loginByQQ({
code,
accessToken,
inviteCode
})
参数说明:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
code | string | 否 | QQ小程序 uni.login 返回的code参数 |
accessToken | string | 否 | APP端QQ登录返回的accessToken参数 |
accessTokenExpired | number | 否 | accessToken过期时间,由APP端QQ登录 返回的expires_in参数 |
inviteCode | string | 否 | 邀请码,仅注册时生效 |
返回值:
参数名 | 类型 | 说明 | |
---|---|---|---|
errCode | string | 错误码 | |
errMsg | string | 错误信息 | |
newToken | object | token信息 | |
token | string | token | |
tokenExpired | string | token过期时间 |
实例()
B、服务端代码
/uni-id-co/module/login/login-by-qq.js
const {initQQ} = require('../../lib/third-party/index') const {ERROR} = require('../../common/error') const {preUnifiedLogin,postUnifiedLogin} = require('../../lib/utils/unified-login') const {LOG_TYPE} = require('../../common/constants') const {getQQPlatform,generateQQCache,saveQQUserKey} = require('../../lib/utils/qq') const url = require('url') /** * QQ登录 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-qq * @param {Object} params * @param {String} params.code QQ小程序登录返回的code参数 * @param {String} params.accessToken App端QQ登录返回的accessToken参数 * @param {String} params.accessTokenExpired accessToken过期时间,由App端QQ登录返回的expires_in参数计算而来,单位:毫秒 * @param {String} params.inviteCode 邀请码 * @returns */ module.exports = async function (params = {}) { const schema = { code: {required: false,type: 'string'}, accessToken: {required: false,type: 'string'}, accessTokenExpired: {required: false,type: 'number'}, inviteCode: {required: false,type: 'string'} } this.middleware.validate(params, schema) const {code,accessToken,accessTokenExpired,inviteCode} = params const {appId} = this.getUniversalClientInfo() const qqApi = initQQ.call(this) const qqPlatform = getQQPlatform.call(this) let apiName switch (qqPlatform) { case 'mp': apiName = 'code2Session' break case 'app': apiName = 'getOpenidByToken' break default: throw new Error('Unsupported qq platform') } let getQQAccountResult try { getQQAccountResult = await qqApi[apiName]({code,accessToken}) } catch (error) { console.error(error) await this.middleware.uniIdLog({success: false,type: LOG_TYPE.LOGIN}) throw {errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED} } const {openid,unionid,sessionKey} = getQQAccountResult const {type,user} = await preUnifiedLogin.call(this, { user: {qq_openid: {[qqPlatform]: openid},qq_unionid: unionid }}) const extraData = {qq_openid: {[`${qqPlatform}_${appId}`]: openid},qq_unionid: unionid} if (type === 'register' && qqPlatform !== 'mp') { const {nickname,avatar} = await qqApi.getUserInfo({accessToken,openid}) // eslint-disable-next-line n/no-deprecated-api const extName = url.parse(avatar).pathname.split('.').pop() const cloudPath = `user/avatar/${openid.slice(-8) + Date.now()}-avatar.${extName}` const getAvatarRes = await uniCloud.httpclient.request(avatar) if (getAvatarRes.status >= 400) { throw {errCode: ERROR.GET_THIRD_PARTY_USER_INFO_FAILED} } const {fileID} = await uniCloud.uploadFile({cloudPath,fileContent: getAvatarRes.data}) extraData.nickname = nickname extraData.avatar_file = {name: cloudPath,extname: extName,url: fileID} } await saveQQUserKey.call(this, {openid,sessionKey,accessToken,accessTokenExpired}) return postUnifiedLogin.call(this, {user, extraData: {...extraData, ...generateQQCache.call(this, {openid,sessionKey, accessToken, accessTokenExpired }) },isThirdParty: true,type,inviteCode }) }
C、问题
暂无
15、loginByApple
功能:苹果账号已存在时登录,否则注册
A、调用端
接口形式:
await uniIdCo.loginByApple({
identityToken,
nickname,
inviteCode
})
参数说明:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
identityToken | string | 是 | App端苹果登录返回的identityTokenc参数 |
nickname | string | 否 | 昵称 |
inviteCode | string | 否 | 邀请码,仅注册时生效 |
返回值:
参数名 | 类型 | 说明 | |
---|---|---|---|
errCode | string | 错误码 | |
errMsg | string | 错误信息 | |
newToken | object | token信息 | |
token | string | token | |
tokenExpired | string | token过期时间 |
实例()
B、服务端代码
/uni-id-co/module/login/login-by-apple.js
const {initApple} = require('../../lib/third-party/index') const {ERROR} = require('../../common/error') const {preUnifiedLogin,postUnifiedLogin} = require('../../lib/utils/unified-login') const {LOG_TYPE} = require('../../common/constants') /** * 苹果登录 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-apple * @param {Object} params * @param {String} params.identityToken 苹果登录返回的identityToken * @param {String} params.nickname 用户昵称 * @param {String} params.inviteCode 邀请码 * @returns */ module.exports = async function (params = {}) { const schema = { identityToken: 'string', nickname: {required: false,type: 'nickname'}, inviteCode: {required: false,type: 'string'} } this.middleware.validate(params, schema) const {identityToken,nickname,inviteCode} = params const appleApi = initApple.call(this) let verifyResult try { verifyResult = await appleApi.verifyIdentityToken(identityToken) } catch (error) { console.error(error) await this.middleware.uniIdLog({success: false,type: LOG_TYPE.LOGIN}) throw {errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED} } const {openid} = verifyResult const {type,user} = await preUnifiedLogin.call(this, {user: {apple_openid: openid}}) return postUnifiedLogin.call(this, {user,extraData: {nickname},isThirdParty: true,type,inviteCode}) }
C、问题
暂无
16、logout
功能:登出
A、调用端
接口形式:
await uniIdCo.logout()
参数说明:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
返回值:
参数名 | 类型 | 说明 |
---|---|---|
errCode | string | 错误码 |
errMsg | string | 错误信息 |
实例()
B、服务端代码
/uni-id-co/module/logout/index.js
module.exports = { logout: require('./logout') }
/uni-id-co/module/logout/logout.js
const {logout} = require('../../lib/utils/logout') /** * 用户退出登录 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#logout * @returns */ module.exports = async function () { await logout.call(this) return { errCode: 0 } }
/lib/utils/logout
const {dbCmd,LOG_TYPE,deviceCollection,userCollection} = require('../../common/constants') async function logout () { const {deviceId} = this.getUniversalClientInfo() const uniIdToken = this.getUniversalUniIdToken() const payload = await this.uniIdCommon.checkToken(uniIdToken,{autoRefresh: false}) if (payload.errCode) { throw payload } const uid = payload.uid // 删除token await userCollection.doc(uid).update({token: dbCmd.pull(uniIdToken)}) // 仅当device表的device_id和user_id均对应时才进行更新 await deviceCollection.where({device_id: deviceId,user_id: uid}).update({token_expired: 0}) await this.middleware.uniIdLog({data: {user_id: uid},type: LOG_TYPE.LOGOUT}) return {errCode: 0} } module.exports = {logout}
C、问题
暂无
17、bindMobileBySms
功能:使用短信验证码绑定手机号
A、调用端
接口形式:
await uniIdCo.bindMobileBySms({
mobile,
code,
captcha
})
参数说明:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
mobile | string | 是 | 手机号码 |
code | string | 是 | 短信验证码 |
captcha | string | 否 | 图形验证码 |
返回值:
参数名 | 类型 | 说明 | |
---|---|---|---|
errCode | string | 错误码 | |
errMsg | string | 错误信息 | |
newToken | object | token信息 | |
token | string | token | |
tokenExpired | string | token过期时间 |
实例()
B、服务端代码
/uni-id-co/module/relate/index.js
module.exports = { bindMobileBySms: require('./bind-mobile-by-sms'), bindMobileByUniverify: require('./bind-mobile-by-univerify'), bindMobileByMpWeixin: require('./bind-mobile-by-mp-weixin'), bindAlipay: require('./bind-alipay'), bindApple: require('./bind-apple'), bindQQ: require('./bind-qq'), bindWeixin: require('./bind-weixin'), unbindWeixin: require('./unbind-weixin'), unbindAlipay: require('./unbind-alipay'), unbindQQ: require('./unbind-qq'), unbindApple: require('./unbind-apple') }
/uni-id-co/module/relate/bind-mobile-by-sms.js
const {getNeedCaptcha,verifyCaptcha} = require('../../lib/utils/captcha') const {LOG_TYPE,SMS_SCENE,CAPTCHA_SCENE} = require('../../common/constants') const {verifyMobileCode} = require('../../lib/utils/verify-code') const {preBind,postBind} = require('../../lib/utils/relate') /** * 通过短信验证码绑定手机号 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-mobile-by-sms * @param {Object} params * @param {String} params.mobile 手机号 * @param {String} params.code 短信验证码 * @param {String} params.captcha 图形验证码 * @returns */ module.exports = async function (params = {}) { const schema = { mobile: 'mobile', code: 'string', captcha: {required: false,type: 'string'} } const {mobile,code,captcha} = params this.middleware.validate(params, schema) const uid = this.authInfo.uid // 判断是否需要验证码 const needCaptcha = await getNeedCaptcha.call(this, {uid,type: LOG_TYPE.BIND_MOBILE}) if (needCaptcha) { await verifyCaptcha.call(this, {captcha,scene: CAPTCHA_SCENE.BIND_MOBILE_BY_SMS}) } try { // 验证手机号验证码,验证不通过时写入失败日志 await verifyMobileCode({mobile,code,scene: SMS_SCENE.BIND_MOBILE_BY_SMS}) } catch (error) { await this.middleware.uniIdLog({ data: {user_id: uid},type: LOG_TYPE.BIND_MOBILE,success: false }) throw error } const bindAccount = {mobile} await preBind.call(this, {uid,bindAccount,logType: LOG_TYPE.BIND_MOBILE}) await postBind.call(this, {uid,bindAccount,extraData: {mobile_confirmed: 1},logType: LOG_TYPE.BIND_MOBILE}) return {errCode: 0} }
C、问题
暂无
18、bindMobileByUniverify
功能:通过一键登录绑定手机号
A、调用端
接口形式:
await uniIdCo.bindMobileByUniverify({
openid,
access_token
})
参数说明:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
openid | string | 是 | 客户端一键登录返回的openid参数 |
access_token | string | 是 | 客户端一键登录返回的access_token参数 |
返回值:
参数名 | 类型 | 说明 | |
---|---|---|---|
errCode | string | 错误码 | |
errMsg | string | 错误信息 | |
newToken | object | token信息 | |
token | string | token | |
tokenExpired | string | token过期时间 |
实例()
B、服务端代码
/uni-id-co/module/relate/bind-mobile-by-univerify.js
const {getPhoneNumber} = require('../../lib/utils/univerify') const {LOG_TYPE} = require('../../common/constants') const {preBind,postBind} = require('../../lib/utils/relate') /** * 通过一键登录绑定手机号 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-mobile-by-univerify * @param {Object} params * @param {String} params.openid APP端一键登录返回的openid * @param {String} params.access_token APP端一键登录返回的access_token * @returns */ module.exports = async function (params = {}) { const schema = {openid: 'string',access_token: 'string'} const {openid,access_token} = params this.middleware.validate(params, schema const uid = this.authInfo.uid let mobile try { const phoneInfo = await getPhoneNumber.call(this, {access_token,openid}) mobile = phoneInfo.phoneNumber } catch (error) { await this.middleware.uniIdLog({success: false,data: {user_id: uid},type: LOG_TYPE.BIND_MOBILE}) throw error } const bindAccount = {mobile} await preBind.call(this, {uid,bindAccount,logType: LOG_TYPE.BIND_MOBILE}) await postBind.call(this, {uid,bindAccount,extraData: {mobile_confirmed: 1},logType: LOG_TYPE.BIND_MOBILE}) return {errCode: 0} }
C、问题
暂无
19、bindMobileByMpWeixin
功能:通过微信绑定手机号
A、调用端
接口形式:
await uniIdCo.bindMobileByMpWeixin({
code
})
参数说明:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
code | string | 是 | 微信小程序获取手机号返回的code参数; |
返回值:
参数名 | 类型 | 说明 | |
---|---|---|---|
errCode | string | 错误码 | |
errMsg | string | 错误信息 | |
newToken | object | token信息 | |
token | string | token | |
tokenExpired | string | token过期时间 |
实例()
B、服务端代码
/uni-id-co/module/relate/index.js
module.exports = { bindMobileBySms: require('./bind-mobile-by-sms'), bindMobileByUniverify: require('./bind-mobile-by-univerify'), bindMobileByMpWeixin: require('./bind-mobile-by-mp-weixin'), bindAlipay: require('./bind-alipay'), bindApple: require('./bind-apple'), bindQQ: require('./bind-qq'), bindWeixin: require('./bind-weixin'), unbindWeixin: require('./unbind-weixin'), unbindAlipay: require('./unbind-alipay'), unbindQQ: require('./unbind-qq'), unbindApple: require('./unbind-apple') }
/uni-id-co/module/relate/bind-mobile-by-mp-weixin.js
const {preBind,postBind} = require('../../lib/utils/relate') const {LOG_TYPE} = require('../../common/constants') const {decryptWeixinData,getWeixinCache, getWeixinAccessToken} = require('../../lib/utils/weixin') const { initWeixin } = require('../../lib/third-party') const { ERROR } = require('../../common/error') /** * 通过微信绑定手机号 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-mobile-by-mp-weixin * @param {Object} params * @param {String} params.encryptedData 微信获取手机号返回的加密信息 * @param {String} params.iv 微信获取手机号返回的初始向量 * @param {String} params.code 微信获取手机号返回的code * @returns */ module.exports = async function (params = {}) { /** * 微信小程序的规则是客户端应先使用checkSession接口检测上次获取的sessionKey是否仍有效 * 如果有效则直接使用上次存储的sessionKey即可 * 如果无效应重新调用login接口再次刷新sessionKey * 因此此接口不应直接使用客户端login获取的code,只能使用缓存的sessionKey */ const schema = { encryptedData: {required: false,type: 'string'}, iv: {required: false,type: 'string'}, code: {required: false,type: 'string'} } const {encryptedData,iv,code} = params this.middleware.validate(params, schema) if ((!encryptedData && !iv) && !code) { return {errCode: ERROR.INVALID_PARAM} } const uid = this.authInfo.uid let mobile if (code) { // 区分客户端类型 小程序还是App const accessToken = await getWeixinAccessToken.call(this) const weixinApi = initWeixin.call(this) const res = await weixinApi.getPhoneNumber(accessToken, code) mobile = res.purePhoneNumber } else { const sessionKey = await getWeixinCache.call(this, {uid,key: 'session_key'}) if (!sessionKey) { throw new Error('Session key not found') } const res = decryptWeixinData.call(this, {encryptedData,sessionKey,iv}) mobile = res.purePhoneNumber } const bindAccount = {mobile} await preBind.call(this, {uid,bindAccount,logType: LOG_TYPE.BIND_MOBILE}) await postBind.call(this, {uid,bindAccount,extraData: {mobile_confirmed: 1},logType: LOG_TYPE.BIND_MOBILE}) return {errCode: 0} }
C、问题
暂无
20、bindWeixin
功能:绑定微信
A、调用端
接口形式:
await uniIdCo.bindWeixin({
code
})
参数说明:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
code | string | 是 | 微信登录返回的code参数 |
返回值:
参数名 | 类型 | 说明 | |
---|---|---|---|
errCode | string | 错误码 | |
errMsg | string | 错误信息 | |
newToken | object | token信息 | |
token | string | token | |
tokenExpired | string | token过期时间 |
实例()
B、服务端代码
/uni-id-co/module/relate/index.js
module.exports = { bindMobileBySms: require('./bind-mobile-by-sms'), bindMobileByUniverify: require('./bind-mobile-by-univerify'), bindMobileByMpWeixin: require('./bind-mobile-by-mp-weixin'), bindAlipay: require('./bind-alipay'), bindApple: require('./bind-apple'), bindQQ: require('./bind-qq'), bindWeixin: require('./bind-weixin'), unbindWeixin: require('./unbind-weixin'), unbindAlipay: require('./unbind-alipay'), unbindQQ: require('./unbind-qq'), unbindApple: require('./unbind-apple') }
/uni-id-co/module/relate/bind-weixin.js
const {preBind,postBind} = require('../../lib/utils/relate') const {LOG_TYPE} = require('../../common/constants') const {generateWeixinCache,saveWeixinUserKey,getWeixinPlatform} = require('../../lib/utils/weixin') const {initWeixin} = require('../../lib/third-party/index') const {ERROR} = require('../../common/error') /** * 绑定微信 * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-weixin * @param {Object} params * @param {String} params.code 微信登录返回的code * @returns */ module.exports = async function (params = {}) { const schema = {code: 'string'} this.middleware.validate(params, schema) const uid = this.authInfo.uid const {code} = params const weixinPlatform = getWeixinPlatform.call(this) const appId = this.getUniversalClientInfo().appId const weixinApi = initWeixin.call(this) const clientPlatform = this.clientPlatform const apiName = clientPlatform === 'mp-weixin' ? 'code2Session' : 'getOauthAccessToken' let getWeixinAccountResult try { getWeixinAccountResult = await weixinApi[apiName](code) } catch (error) { await this.middleware.uniIdLog({success: false,type: LOG_TYPE.BIND_WEIXIN}) throw {errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED} } const {openid,unionid, sessionKey, // 微信小程序用户sessionKey accessToken, // App端微信用户accessToken refreshToken, // App端微信用户refreshToken expired: accessTokenExpired // App端微信用户accessToken过期时间 } = getWeixinAccountResult const bindAccount = {wx_openid: {[weixinPlatform]: openid},wx_unionid: unionid} await preBind.call(this, {uid,bindAccount,logType: LOG_TYPE.BIND_WEIXIN}) await saveWeixinUserKey.call(this, {openid,sessionKey,accessToken,refreshToken,accessTokenExpired}) return postBind.call(this, {uid,bindAccount, extraData: { wx_openid: {[`${weixinPlatform}_${appId}`]: openid}, ...generateWeixinCache.call(this, {openid,sessionKey, // 微信小程序用户sessionKey accessToken, // App端微信用户accessToken refreshToken, // App端微信用户refreshToken accessTokenExpired // App端微信用户accessToken过期时间 }) }, logType: LOG_TYPE.BIND_WEIXIN }) }
C、问题
暂无
21、bindQQ
功能:绑定QQ
A、调用端
接口形式:
await uniIdCo.bindQQ({
code,
accessToken
})
参数说明:
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
code | string | 否 | QQ小程序登录返回的code参数 |
accessToken | string | 否 | App端QQ登录返回的accessToken参数 |
返回值:
参数名 | 类型 | 说明 |
---|---|---|
实例()
B、服务端代码
/uni-id-co/module/relate/bind-qq.js
const {preBind,postBind} = require('../../lib/utils/relate') const {LOG_TYPE} = require('../../common/constants') const {ERROR} = require('../../common/error') const {initQQ} = require('../../lib/third-party/index') const {generateQQCache,getQQPlatform,saveQQUserKey} = require('../../lib/utils/qq') /** * 绑定QQ * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-qq * @param {Object} params * @param {String} params.code 小程序端QQ登录返回的code * @param {String} params.accessToken APP端QQ登录返回的accessToken * @param {String} params.accessTokenExpired accessToken过期时间,由App端QQ登录返回的expires_in参数计算而来,单位:毫秒 * @returns */ module.exports = async function (params = {}) { const schema = { code: {required: false,type: 'string'}, accessToken: {required: false,type: 'string'}, accessTokenExpired: {required: false,type: 'number'} } this.middleware.validate(params, schema) const uid = this.authInfo.uid const {code,accessToken,accessTokenExpired} = params const qqPlatform = getQQPlatform.call(this) const appId = this.getUniversalClientInfo().appId const qqApi = initQQ.call(this) const clientPlatform = this.clientPlatform const apiName = clientPlatform === 'mp-qq' ? 'code2Session' : 'getOpenidByToken' let getQQAccountResult try { getQQAccountResult = await qqApi[apiName]({code,accessToken}) } catch (error) { await this.middleware.uniIdLog({success: false,type: LOG_TYPE.BIND_QQ}) throw {errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED} } const {openid,unionid,sessionKey // 微信小程序用户sessionKey } = getQQAccountResult const bindAccount = {qq_openid: {[qqPlatform]: openid},qq_unionid: unionid} await preBind.call(this, {uid,bindAccount,logType: LOG_TYPE.BIND_QQ}) await saveQQUserKey.call(this, {openid,sessionKey,accessToken,accessTokenExpired}) return postBind.call(this, {uid,bindAccount, extraData: { qq_openid: {[`${qqPlatform}_${appId}`]: openid}, ...generateQQCache.call(this, {openid,sessionKey}) }, logType: LOG_TYPE.BIND_QQ }) }
…/…/lib/utils/relate
C、问题
暂无