uni-id 身份认证

174 阅读10分钟

uni-id 身份认证

uni-id 是 uniCloud 提供的身份认证服务,它提供了一套完整的用户身份管理解决方案,包括用户注册、登录、密码重置、角色权限管理等功能。本文将详细介绍 uni-id 的使用方法,帮助开发者快速实现应用的身份认证功能。

1. uni-id 概述

1.1 什么是 uni-id

uni-id 是 uniCloud 提供的身份认证服务,它基于 JWT(JSON Web Token)技术,提供了一套完整的用户身份管理解决方案。uni-id 支持多种登录方式,包括账号密码登录、手机号登录、邮箱登录、第三方登录等,可以满足各种应用场景的需求。

1.2 uni-id 的优势

  • 开箱即用:uni-id 已经封装了常用的身份认证功能,开发者无需从零开始实现
  • 多端支持:支持 H5、App、小程序等多种平台
  • 安全可靠:基于 JWT 技术,支持 token 自动续期,安全性高
  • 灵活扩展:支持自定义登录方式、自定义字段、自定义权限等
  • 与 uniCloud 无缝集成:与 uniCloud 的云函数、云数据库、云存储等服务无缝集成

1.3 uni-id 的功能

uni-id 提供以下核心功能:

  • 用户管理:用户注册、登录、注销、信息修改等
  • 身份验证:账号密码验证、手机号验证、邮箱验证等
  • 权限管理:角色管理、权限分配、权限验证等
  • 第三方登录:微信、QQ、支付宝等第三方平台登录
  • 安全机制:密码加密、token 管理、登录限制等

2. uni-id 的安装与配置

2.1 安装 uni-id

uni-id 是 uniCloud 的扩展库,需要先安装:

# 在项目根目录下执行
npm install uni-id

2.2 初始化 uni-id

在云函数中初始化 uni-id:

// 云函数入口文件
const uniID = require('uni-id')

exports.main = async (event, context) => {
  // 初始化 uni-id
  const uniIDInstance = new uniID({
    context: context
  })
  
  // 调用 uni-id 的方法
  const result = await uniIDInstance.login({
    username: event.username,
    password: event.password
  })
  
  return result
}

2.3 配置 uni-id

uni-id 支持多种配置选项,可以通过配置文件进行设置:

// uni-id 配置文件 (uni-id/config.json)
{
  "passwordSecret": "passwordSecret-demo", // 密码加密密钥
  "tokenSecret": "tokenSecret-demo", // token 加密密钥
  "tokenExpiresIn": 7200, // token 过期时间,单位为秒
  "tokenExpiresThreshold": 3600, // token 过期阈值,单位为秒
  "passwordErrorLimit": 6, // 密码错误次数限制
  "passwordErrorRetryTime": 3600, // 密码错误重试时间,单位为秒
  "autoSetInviteCode": false, // 是否自动设置邀请码
  "forceInviteCode": false, // 是否强制使用邀请码
  "app-plus": {
    "tokenExpiresIn": 2592000, // App 端 token 过期时间,单位为秒
    "oauth": {
      "weixin": {
        "appid": "weixin-appid",
        "appsecret": "weixin-appsecret"
      }
    }
  },
  "mp-weixin": {
    "oauth": {
      "weixin": {
        "appid": "weixin-appid",
        "appsecret": "weixin-appsecret"
      }
    }
  },
  "h5": {
    "oauth": {
      "weixin": {
        "appid": "weixin-appid",
        "appsecret": "weixin-appsecret"
      }
    }
  }
}

2.4 数据库初始化

uni-id 需要特定的数据库集合,可以通过以下命令初始化:

# 在项目根目录下执行
npx uni-id init

这将创建以下集合:

  • uni-id-users:用户信息集合
  • uni-id-tokens:token 信息集合
  • uni-id-permissions:权限信息集合
  • uni-id-roles:角色信息集合

3. uni-id 基本功能

3.1 用户注册

3.1.1 账号密码注册
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.register({
    username: event.username,
    password: event.password,
    nickname: event.nickname,
    gender: event.gender,
    avatar: event.avatar
  })
  
  return result
}
// 前端调用
uniCloud.callFunction({
  name: 'register',
  data: {
    username: 'username',
    password: 'password',
    nickname: 'nickname',
    gender: 1,
    avatar: 'avatar-url'
  },
  success: function(res) {
    console.log(res.result)
  },
  fail: function(err) {
    console.error(err)
  }
})
3.1.2 手机号注册
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.register({
    mobile: event.mobile,
    password: event.password,
    code: event.code // 短信验证码
  })
  
  return result
}
3.1.3 邮箱注册
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.register({
    email: event.email,
    password: event.password,
    code: event.code // 邮箱验证码
  })
  
  return result
}

3.2 用户登录

3.2.1 账号密码登录
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.login({
    username: event.username,
    password: event.password
  })
  
  return result
}
// 前端调用
uniCloud.callFunction({
  name: 'login',
  data: {
    username: 'username',
    password: 'password'
  },
  success: function(res) {
    console.log(res.result)
    // 保存 token
    uni.setStorageSync('uni_id_token', res.result.token)
    uni.setStorageSync('uni_id_token_expired', res.result.tokenExpired)
  },
  fail: function(err) {
    console.error(err)
  }
})
3.2.2 手机号登录
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.loginByMobile({
    mobile: event.mobile,
    code: event.code // 短信验证码
  })
  
  return result
}
3.2.3 邮箱登录
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.loginByEmail({
    email: event.email,
    code: event.code // 邮箱验证码
  })
  
  return result
}

3.3 用户注销

// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.logout({
    token: event.token
  })
  
  return result
}
// 前端调用
uniCloud.callFunction({
  name: 'logout',
  data: {
    token: uni.getStorageSync('uni_id_token')
  },
  success: function(res) {
    console.log(res.result)
    // 清除 token
    uni.removeStorageSync('uni_id_token')
    uni.removeStorageSync('uni_id_token_expired')
  },
  fail: function(err) {
    console.error(err)
  }
})

3.4 获取用户信息

// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.checkToken({
    token: event.token
  })
  
  if (result.code === 0) {
    // token 有效,返回用户信息
    return {
      code: 0,
      message: '获取用户信息成功',
      userInfo: result.userInfo
    }
  } else {
    // token 无效
    return {
      code: result.code,
      message: result.message
    }
  }
}
// 前端调用
uniCloud.callFunction({
  name: 'getUserInfo',
  data: {
    token: uni.getStorageSync('uni_id_token')
  },
  success: function(res) {
    console.log(res.result)
    if (res.result.code === 0) {
      // 保存用户信息
      uni.setStorageSync('userInfo', res.result.userInfo)
    }
  },
  fail: function(err) {
    console.error(err)
  }
})

3.5 修改用户信息

// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.updateUser({
    uid: event.uid,
    nickname: event.nickname,
    gender: event.gender,
    avatar: event.avatar
  })
  
  return result
}

3.6 修改密码

// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.updatePwd({
    uid: event.uid,
    oldPassword: event.oldPassword,
    newPassword: event.newPassword
  })
  
  return result
}

3.7 重置密码

// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.resetPwd({
    mobile: event.mobile,
    code: event.code, // 短信验证码
    password: event.password
  })
  
  return result
}

4. uni-id 高级功能

4.1 第三方登录

4.1.1 微信登录
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.loginByWeixin({
    code: event.code
  })
  
  return result
}
// 前端调用
// 获取微信登录 code
uni.login({
  provider: 'weixin',
  success: function(loginRes) {
    const code = loginRes.code
    
    // 调用云函数登录
    uniCloud.callFunction({
      name: 'loginByWeixin',
      data: {
        code: code
      },
      success: function(res) {
        console.log(res.result)
        // 保存 token
        uni.setStorageSync('uni_id_token', res.result.token)
        uni.setStorageSync('uni_id_token_expired', res.result.tokenExpired)
      },
      fail: function(err) {
        console.error(err)
      }
    })
  },
  fail: function(err) {
    console.error(err)
  }
})
4.1.2 QQ 登录
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.loginByQQ({
    code: event.code
  })
  
  return result
}
4.1.3 支付宝登录
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.loginByAlipay({
    code: event.code
  })
  
  return result
}

4.2 角色权限管理

4.2.1 创建角色
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.createRole({
    roleID: event.roleID,
    roleName: event.roleName,
    permission: event.permission
  })
  
  return result
}
4.2.2 删除角色
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.deleteRole({
    roleID: event.roleID
  })
  
  return result
}
4.2.3 修改角色
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.updateRole({
    roleID: event.roleID,
    roleName: event.roleName,
    permission: event.permission
  })
  
  return result
}
4.2.4 获取角色列表
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.getRoleList({
    limit: event.limit,
    offset: event.offset
  })
  
  return result
}
4.2.5 为用户分配角色
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.addUserRole({
    uid: event.uid,
    roleID: event.roleID
  })
  
  return result
}
4.2.6 移除用户角色
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.removeUserRole({
    uid: event.uid,
    roleID: event.roleID
  })
  
  return result
}
4.2.7 检查用户权限
// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.checkPermission({
    uid: event.uid,
    permissionID: event.permissionID
  })
  
  return result
}

4.3 自定义字段

uni-id 支持自定义用户字段,可以在注册和更新用户信息时添加自定义字段:

// 云函数
const uniID = require('uni-id')

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.register({
    username: event.username,
    password: event.password,
    // 自定义字段
    customField1: event.customField1,
    customField2: event.customField2
  })
  
  return result
}

4.4 自定义登录方式

uni-id 支持自定义登录方式,可以通过扩展 uni-id 实现:

// 云函数
const uniID = require('uni-id')

// 扩展 uni-id
uniID.prototype.loginByCustom = async function(params) {
  // 自定义登录逻辑
  const { customField, customValue } = params
  
  // 查询用户
  const user = await this.db.collection('uni-id-users').where({
    [customField]: customValue
  }).limit(1).get()
  
  if (user.data.length === 0) {
    return {
      code: 10001,
      message: '用户不存在'
    }
  }
  
  // 生成 token
  const token = await this.createToken({
    uid: user.data[0]._id
  })
  
  return {
    code: 0,
    message: '登录成功',
    token: token.token,
    tokenExpired: token.tokenExpired,
    userInfo: user.data[0]
  }
}

exports.main = async (event, context) => {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.loginByCustom({
    customField: event.customField,
    customValue: event.customValue
  })
  
  return result
}

5. uni-id 安全性

5.1 密码加密

uni-id 使用 bcrypt 算法对密码进行加密,确保密码安全存储:

// 密码加密示例
const bcrypt = require('bcrypt')
const saltRounds = 10

// 加密密码
const hash = bcrypt.hashSync(password, saltRounds)

// 验证密码
const isMatch = bcrypt.compareSync(password, hash)

5.2 Token 管理

uni-id 使用 JWT 技术管理 token,支持 token 自动续期:

// token 配置
{
  "tokenSecret": "tokenSecret-demo", // token 加密密钥
  "tokenExpiresIn": 7200, // token 过期时间,单位为秒
  "tokenExpiresThreshold": 3600 // token 过期阈值,单位为秒
}

5.3 登录限制

uni-id 支持登录限制,可以限制密码错误次数和重试时间:

// 登录限制配置
{
  "passwordErrorLimit": 6, // 密码错误次数限制
  "passwordErrorRetryTime": 3600 // 密码错误重试时间,单位为秒
}

5.4 权限控制

uni-id 提供基于角色的权限控制,可以精细控制用户权限:

// 权限检查
const result = await uniIDInstance.checkPermission({
  uid: uid,
  permissionID: 'permission-id'
})

if (result.code === 0) {
  // 有权限
} else {
  // 无权限
}

6. uni-id 最佳实践

6.1 前端封装

为了方便使用,可以在前端封装 uni-id 的调用:

// uni-id 前端封装
const uniID = {
  // 登录
  login: function(username, password) {
    return new Promise((resolve, reject) => {
      uniCloud.callFunction({
        name: 'login',
        data: {
          username: username,
          password: password
        },
        success: function(res) {
          if (res.result.code === 0) {
            // 保存 token
            uni.setStorageSync('uni_id_token', res.result.token)
            uni.setStorageSync('uni_id_token_expired', res.result.tokenExpired)
            resolve(res.result)
          } else {
            reject(res.result)
          }
        },
        fail: function(err) {
          reject(err)
        }
      })
    })
  },
  
  // 注册
  register: function(username, password, nickname) {
    return new Promise((resolve, reject) => {
      uniCloud.callFunction({
        name: 'register',
        data: {
          username: username,
          password: password,
          nickname: nickname
        },
        success: function(res) {
          resolve(res.result)
        },
        fail: function(err) {
          reject(err)
        }
      })
    })
  },
  
  // 注销
  logout: function() {
    return new Promise((resolve, reject) => {
      const token = uni.getStorageSync('uni_id_token')
      
      uniCloud.callFunction({
        name: 'logout',
        data: {
          token: token
        },
        success: function(res) {
          // 清除 token
          uni.removeStorageSync('uni_id_token')
          uni.removeStorageSync('uni_id_token_expired')
          resolve(res.result)
        },
        fail: function(err) {
          reject(err)
        }
      })
    })
  },
  
  // 获取用户信息
  getUserInfo: function() {
    return new Promise((resolve, reject) => {
      const token = uni.getStorageSync('uni_id_token')
      
      uniCloud.callFunction({
        name: 'getUserInfo',
        data: {
          token: token
        },
        success: function(res) {
          resolve(res.result)
        },
        fail: function(err) {
          reject(err)
        }
      })
    })
  },
  
  // 检查登录状态
  checkLogin: function() {
    const token = uni.getStorageSync('uni_id_token')
    const tokenExpired = uni.getStorageSync('uni_id_token_expired')
    
    if (!token || !tokenExpired) {
      return false
    }
    
    if (Date.now() > tokenExpired) {
      // token 已过期
      uni.removeStorageSync('uni_id_token')
      uni.removeStorageSync('uni_id_token_expired')
      return false
    }
    
    return true
  }
}

// 使用示例
async function login() {
  try {
    const result = await uniID.login('username', 'password')
    console.log('登录成功', result)
  } catch (err) {
    console.error('登录失败', err)
  }
}

6.2 云函数封装

为了方便使用,可以在云函数中封装 uni-id 的调用:

// uni-id 云函数封装
const uniID = require('uni-id')

// 初始化 uni-id
function initUniID(context) {
  return new uniID({
    context: context
  })
}

// 登录
async function login(event, context) {
  const uniIDInstance = initUniID(context)
  
  const result = await uniIDInstance.login({
    username: event.username,
    password: event.password
  })
  
  return result
}

// 注册
async function register(event, context) {
  const uniIDInstance = initUniID(context)
  
  const result = await uniIDInstance.register({
    username: event.username,
    password: event.password,
    nickname: event.nickname
  })
  
  return result
}

// 注销
async function logout(event, context) {
  const uniIDInstance = initUniID(context)
  
  const result = await uniIDInstance.logout({
    token: event.token
  })
  
  return result
}

// 获取用户信息
async function getUserInfo(event, context) {
  const uniIDInstance = initUniID(context)
  
  const result = await uniIDInstance.checkToken({
    token: event.token
  })
  
  if (result.code === 0) {
    return {
      code: 0,
      message: '获取用户信息成功',
      userInfo: result.userInfo
    }
  } else {
    return {
      code: result.code,
      message: result.message
    }
  }
}

// 云函数入口
exports.main = async (event, context) => {
  const { action, data } = event
  
  switch (action) {
    case 'login':
      return await login(data, context)
    case 'register':
      return await register(data, context)
    case 'logout':
      return await logout(data, context)
    case 'getUserInfo':
      return await getUserInfo(data, context)
    default:
      return {
        code: 10001,
        message: '未知的操作类型'
      }
  }
}

6.3 权限控制最佳实践

6.3.1 基于角色的权限控制
// 定义角色
const roles = {
  admin: {
    roleID: 'admin',
    roleName: '管理员',
    permission: ['*'] // 所有权限
  },
  user: {
    roleID: 'user',
    roleName: '普通用户',
    permission: ['read', 'write'] // 读写权限
  },
  guest: {
    roleID: 'guest',
    roleName: '访客',
    permission: ['read'] // 只读权限
  }
}

// 创建角色
async function createRoles(context) {
  const uniIDInstance = new uniID({
    context: context
  })
  
  for (const roleID in roles) {
    const role = roles[roleID]
    await uniIDInstance.createRole({
      roleID: role.roleID,
      roleName: role.roleName,
      permission: role.permission
    })
  }
}

// 检查权限
async function checkPermission(uid, permissionID, context) {
  const uniIDInstance = new uniID({
    context: context
  })
  
  const result = await uniIDInstance.checkPermission({
    uid: uid,
    permissionID: permissionID
  })
  
  return result.code === 0
}
6.3.2 权限中间件
// 权限中间件
async function permissionMiddleware(event, context) {
  const { action, data } = event
  
  // 不需要权限的操作
  const publicActions = ['login', 'register', 'resetPwd']
  if (publicActions.includes(action)) {
    return event
  }
  
  // 获取 token
  const token = data.token || context.TOKEN
  
  if (!token) {
    return {
      code: 10002,
      message: '未登录'
    }
  }
  
  // 检查 token
  const uniIDInstance = new uniID({
    context: context
  })
  
  const tokenResult = await uniIDInstance.checkToken({
    token: token
  })
  
  if (tokenResult.code !== 0) {
    return {
      code: tokenResult.code,
      message: tokenResult.message
    }
  }
  
  // 将用户信息添加到 context
  context.USER_INFO = tokenResult.userInfo
  
  return event
}

// 云函数入口
exports.main = async (event, context) => {
  // 权限中间件
  const middlewareResult = await permissionMiddleware(event, context)
  
  if (middlewareResult.code) {
    return middlewareResult
  }
  
  const { action, data } = middlewareResult
  
  // 处理请求
  switch (action) {
    case 'login':
      return await login(data, context)
    case 'register':
      return await register(data, context)
    case 'logout':
      return await logout(data, context)
    case 'getUserInfo':
      return await getUserInfo(data, context)
    default:
      return {
        code: 10001,
        message: '未知的操作类型'
      }
  }
}

6.4 多端适配

uni-id 支持多端适配,可以为不同平台配置不同的登录方式:

// 多端配置
{
  "app-plus": {
    "tokenExpiresIn": 2592000, // App 端 token 过期时间,单位为秒
    "oauth": {
      "weixin": {
        "appid": "weixin-appid",
        "appsecret": "weixin-appsecret"
      }
    }
  },
  "mp-weixin": {
    "oauth": {
      "weixin": {
        "appid": "weixin-appid",
        "appsecret": "weixin-appsecret"
      }
    }
  },
  "h5": {
    "oauth": {
      "weixin": {
        "appid": "weixin-appid",
        "appsecret": "weixin-appsecret"
      }
    }
  }
}

7. 总结

uni-id 是 uniCloud 提供的身份认证服务,它提供了一套完整的用户身份管理解决方案,包括用户注册、登录、密码重置、角色权限管理等功能。通过本文的介绍,您应该已经了解了 uni-id 的基本用法和高级功能,可以在实际开发中灵活运用。

uni-id 的优势在于开箱即用、多端支持、安全可靠、灵活扩展和与 uniCloud 无缝集成。通过合理使用 uni-id,可以快速实现应用的身份认证功能,提高开发效率,降低开发成本。

在实际开发中,可以根据业务需求,合理使用 uni-id 的各种功能,构建安全、可靠的身份认证系统。