Authing 文档
文档
快速开始
概念
使用指南
开发集成
应用集成
加入 APN
多租户(内测版)
旧版
快速开始
概念
使用指南
开发集成
应用集成
加入 APN
多租户(内测版)
旧版
使用指南
  • 快速开始

  • 对用户进行认证
  • 对用户进行权限管理
  • 授权
  • 管理用户账号
  • 管理用户目录
  • 同步中心(BETA)
  • 自建应用
  • ASA 表单代填
  • 单点登录 SSO
  • 成为联邦认证身份源
  • 连接外部身份源(IdP)
    • 社会化登录
    • 企业身份源
    • 身份源连接的账号匹配规则
    • 自定义数据库
      • 配置数据库连接和编写脚本
      • 惰性迁移用户到 Authing
      • 完全使用自定义数据库保存用户数据
      • 最佳实践
  • 打通微信生态
  • 迁移用户到 Authing
  • 管理组织机构
  • 自动化
  • 审计日志
  • 配置安全信息

  • 配置用户池信息

  • 部署方案
  • 常见问题 FAQs

    ¶ 配置数据库连接和编写脚本

    更新时间: 2021-06-17 12:49:23

    你可以自定义数据库认证用户,为此你需要配置数据库连接、编写数据库操作脚本、配置环境变量(可选)。

    请确保你的数据库有 Authing 用户资料必须的一些字段,如 id, email, photo, nickname, username, phone 等,详情请见Authing 用户 Profile 详细字段及其释义 了解 Authing 用户资料 Schema。

    Authing 支持直接连接以下几种主流的数据库以及自定义 Web Service:

    • MySQL
    • PostgresQL
    • MongoDB
    • Web Services: 你可以将数据库封装成一个服务,不直接对外暴露数据库连接,如你可以把认证操作封装成一个 API: https://mydomain.com/auth。

    如果你在脚本中访问的服务配置有防火墙,请确保其对以下 IP 地址开放:140.179.19.50, 52.80.250.250 . 你还可以通过以下 API 动态获取 Authing 服务器对外 IP 地址:https://core.authing.cn/api/v2/system/public-ips (opens new window)。

    ¶ 开启自定义数据库连接

    开启自定义数据库后会立即生效,在你还没有编写自定义脚本之前,用户尝试登录会提示类似 Please implement the Login script for this database connection 的错误,这是从默认的脚本提示的错误,请前往修改脚本,或暂时关闭自定义数据库连接。

    登录 Authing 控制台 (opens new window),在 连接身份源 - 自定义数据库 页面,点击右上角的开启按钮:

    ¶ 选择场景

    自定义数据库有两种不同模式,你可以根据自己的业务需求选择合适的模式:

    • 惰性迁移用户:这种迁移用户的模式称为惰性迁移(lazy migration),简单来说原理如下:最开始所有的原始用户数据在你的数据库,当用户第一次尝试在 Authing 登录时,Authing 会通过你配置的自定义数据库脚本在你的数据库查找并验证用户,如果成功,会将该用户迁移到 Authing 中;该用户第二次登录时,将使用 Authing 的数据库对其进行验证;当所有的用户都至少登录一次时,意味着迁移上云任务完成。详情请见:使用自定义数据库实现用户惰性迁移 (opens new window)。
    • 完全使用自定义数据库:这种模式下用户数据始终保存在你的数据库中,Authing 永远不会保存你的用户数据。为了让系统正常工作,你需要实现完整的用户增删改查脚本。

    ¶ 配置数据库连接信息

    接下来填写数据库连接信息,你可以选择 Connection URI 和 Programmatic 两种方式:

    ¶ Connection URI 形式

    Connection URI 一般是 protocol://username:password@host:port/database 这种形式的 URI,如 postgres://postgres:postgres@localhost:5432/database-name,你可以在脚本中通过全局变量 env.DB_CONNECTION_URI 引用,如下所示:

    const { Client } = require('pg')
    
    const client = new Client({
      connectionString: env.DB_CONNECTION_URI,
    })
    await client.connect()
    

    ¶ Programmatic 形式

    这种模式分别指定数据库 Host、端口、用户名、密码、database 名称,可以分别在脚本中通过全局变量 env.DB_HOST、env.DB_PORT、env.DB_USERNAME、env.DB_PASSWORD、env.DB_DATABASE 引用,如下所示:

    const { Client } = require('pg')
    const client = new Client({
      host: env.DB_HOST,
      port: env.DB_PORT,
      user: env.DB_USERNAME,
      password: env.DB_PASSWORD,
      database: env.DB_DATABASE,
    })
    await client.connect()
    

    ¶ 编写数据库操作脚本

    我们目前提供了 MySQL、PostgresQL、MongoDB、自定义服务 四种方式的模版,你可以根据需要编写对应的脚本。

    选择 迁移用户数据到 Authing(LAZY_MIGRATION) 模式时,你一共需要编写两个函数:

    • 登录:该脚本会在用户尝试登录的时候执行,如果该用户未同步到 Authing 数据库,会根据你填写的此脚本检验用户账号密码。
    • 查找用户:该脚本会在用户尝试注册的时候执行,如果该脚本返回了用户身份信息,会提示用户用户已存在而注册失败。

    选择 完全使用自己的数据库(CUSTOM_USER_STORE) 模式时,你一共需要编写以下几个函数:

    • 登录:该脚本会在用户尝试登录的时候执行,如果该用户未同步到 Authing 数据库,会根据你填写的此脚本检验用户账号密码。
    • 精确查找用户:该脚本会在用户尝试注册的时候执行,如果该脚本返回了用户身份信息,会提示用户用户已存在而注册失败。
    • 模糊搜索用户:该脚本会在管理员使用控制台或者 API 模糊搜索用户的时候调用。
    • 创建用户:该脚本会在用户注册或者使用 API 和控制台创建用户时执行,你需要将用户信息保存到自己的数据库中。
    • 修改用户资料:该脚本会在管理员修改用户资料或者用户自己修改用户资料时执行。
    • 删除用户:该脚本会在管理员使用控制台或者 API 删除用户账号时执行。
    • 获取用户列表:该脚本会在管理员使用控制台或者 API 获取用户列表时执行,这个接口需要的数据中需要包含用户总数、当前页用户列表。
    • 验证密码:该脚本会在用户尝试重置密码,验证其原始密码时执行。
    选择脚本名称
    登录
    Loading...

      该脚本会在用户尝试登录的时候执行,如果该用户未同步到 Authing 数据库,会根据你填写的此脚本检验用户账号密码。此脚本在惰性迁移用户和完全使用自定义数据库模式中都需要。

      ¶ 函数定义

      login 函数定义如下:

      async function login(query, password, context) {
        // The first argument query contains following contents:
        // query.email
        // query.username
        // query.phone
      
        // The second argument password is user's password in plain text format.
      
        // The Second argument context contains information about the authentication context.
        // see http://core.authing.cn/connections/custom-db/config-custom-db-connection.html for more information.
      
        // This script should retrieve a user profile from your existing database,
        // without authenticating the user.
      
        // It is used to check if a user exists before executing flows that do not
        // require authentication (signup and password reset).
        //
        // There are three ways this script can finish:
        // 1. A user was successfully found, and password is valid, The profile should be in the following
        // format: https://docs.authing.co/user/profile.html .
        //    return profile
        // 2. A user was not found
        //    throw new Error('User not exists!');
        // 3. A user was found, but password is not valid
        //    throw new Error('Password is not valid!');
        // 4. Something went wrong while trying to reach your database:
        //    throw new Error("my error message")
      
        const msg = 'Please implement the Login script for this database connection';
        throw new Error(msg);
      }
      
      参数 类型 nullable 说明
      query object false 查询条件
      query.email string ture 邮箱。当用户使用邮箱登录时该参数不为空。
      query.phone string true 手机号。当用户使用手机号登录时该参数不为空。
      query.username string true 用户名。当用户使用用户名登录时该参数不为空。
      password string false 明文密码。强烈推荐使用 bcrypt 加密用户密码,详情见下文。
      context object true 请求上下文 context

      其中 context 中包含包含以下信息:

      属性名 类型 说明
      userPoolId string 用户池 ID
      userPoolName string 用户池 名称
      userPoolMetadata object 用户池配置信息
      appId string 当前用户的 ID,你可以通过 appId 区分用户请求的应用来源。
      appName string 当前应用的 名称
      appMetadata object 当前应用的配置信息
      application string 用户池 ID
      request object 当前请求的详细信息,包括:
      ip: 客户端 IP
      geo: 通过 IP 解析的客户端地理位置
      body: 请求体

      ¶ 返回数据约定

      ¶ 用户存在且密码正确

      当用户存在且密码正确时,你需要返回该用户的用户信息给 Authing,用户信息的详细格式请见:用户 Profile 详细字段 。示例:

      async function login(query, password, context) {
        // Implement your logic here
        return {
          id: 1, // must not empty
          email: "test@example.com",
          emailVerified: true,
          nickname: "Nick",
          photo: ""
        }
      }
      

      ¶ 用户不存在

      当用户不存在时,你需要直接抛出错误(错误信息可自由定义),例如:

      async function login(query, password, context) {
        // Implement your logic here
        throw new Error('User not exists');
      }
      

      ¶ 用户存在但密码不正确

      当用户存在但密码不正确,你需要直接抛出错误(错误信息可自由定义),例如:

      async function login(query, password, context) {
        // Implement your logic here
        throw new Error('User not exists');
      }
      

      ¶ 其他异常错误

      当遇到其他异常错误时,你可以捕捉错误之后返回更友好的错误提示,例如:

      async function login(query, password, context) {
        try {
          // Implement your logic here
        } catch (error) {
          throw new Error('Something went wrong ...')
        }
      }
      

      ¶ 最佳实践

      ¶ 提供友好的错误提示

      当遇到未知错误时,我们推荐使用抛出一个标准的 Error 对象,Authing 会捕捉此错误并最终返回给终端用户。例如:throw new Error("My nice error message"),你可以在自定义数据库的 日志历史 中看到该错误日志。

      ¶ 使用 bcrypt 加密密码

      我们推荐使用 bcrypt 加密保存用户数据,如:

      const bcrypt = require('bcrypt');
      const hashedPassword = await bcrypt.hash(
        'passw0rd',
        await bcrypt.genSalt(10),
      );
      

      验证密码是否正确:

      const bcrypt = require('bcrypt');
      const valid = await bcrypt.compare('passw0rd', user.password);
      

      ¶ 函数结束时断开数据库连接

      请切记脚本执行完成时关闭到数据库的连接,比如调用 client.end(). 例如可以在 try/finallly 中执行确保其始终会被执行:

      try {
        const result = await client.query("YOUR QUERY");
      } finally {
        // NOTE: always call `client.end()` here to close the connection to the database
        client.end();
      }
      

      ¶ 示例函数

      以 postgres 数据库为例,有以下几点说明:

      • 你可以通过 env.DB_CONNECTION_URI 获取数据库连接字符串用于创建数据库连接。
      • 根据 query 中传过来的查询条件动态创建查询语句(query.email, query.username, query.phone 三者都可能为空,但不会同时为空)。
      • 如果用户不存在,直接抛出异常,错误信息为 User not exists!。
      • 如果用户存在但密码不正确,直接抛出异常,错误信息为 Password is not valid!。
      • 最后返回指定格式的用户信息,用户信息的详细格式请见:用户 Profile 详细字段。
      • 在 try/finally 中调用 client.end() 断开数据库连接。
      async function login(query, password, context) {
        // The first argument query contains following contents:
        // query.email
        // query.username
        // query.phone
        const { email, username, phone } = query;
      
        // The second argument password is user's password in plain text format.
      
        // The last argument context contains information about the authentication context.
        // see http://core.authing.cn/connections/custom-db/config-custom-db-connection.html for more information.
      
        // This example uses the "pg" library
        // more info here: https://github.com/brianc/node-postgres
        const { Client } = require('pg');
      
        const client = new Client({
          connectionString: env.DB_CONNECTION_URI,
        });
      
        // Or you can:
        // const client = new Client({
        //   host: env.DB_HOST,
        //   port: env.DB_PORT,
        //   user: env.DB_USERNAME,
        //   password: env.DB_PASSWORD,
        //   database: env.DB_DATABASE,
        // });
      
        await client.connect();
      
        // Use bcrypt to validate password
        // more info here: https://github.com/kelektiv/node.bcrypt.js
        const bcrypt = require('bcrypt');
      
        // 构建查询参数
        const queries = [];
        const parameters = [];
        let index = 1;
        if (email) {
          queries.push(`email = $${index}`);
          parameters.push(email);
          index += 1;
        }
        if (phone) {
          queries.push(`phone = $${index}`);
          parameters.push(phone);
          index += 1;
        }
        if (username) {
          queries.push(`username = $${index}`);
          parameters.push(username);
          index += 1;
        }
      
        const QUERY = `SELECT * FROM users WHERE ${queries.join(' OR ')}`;
      
        try {
          const result = await client.query(QUERY, parameters);
          if (result.rows.length === 0) {
            throw new Error('User not exists!');
          }
          const user = result.rows[0];
      
          const isPasswordValid = await bcrypt.compare(password, user.password);
          if (!isPasswordValid) {
            throw new Error('Password is not valid!');
          }
      
          return {
            id: user.id,
            email: user.email,
            phone: user.phone,
            username: user.username,
            photo: user.photo,
            nickname: user.nickname,
            token: user.token,
            emailVerified: user.email_verified,
            phoneVerified: user.phone_verified,
            loginsCount: user.logins_count,
            lastIp: user.last_ip,
            gender: user.gender,
            address: user.address,
            company: user.company,
            birthdate: user.birthdate,
            website: user.website,
          };
        } catch (error) {
          throw new Error(`Execute query failed: ${error.message}`);
        } finally {
          // NOTE: always call `client.end()` here to close the connection to the database
          client.end();
        }
      }
      
      

      ¶ 测试数据库脚本

      我们给开发者提供了一个供快速测试测试的在线数据库,你可以使用此数据库进行快速测试,点击此 (opens new window)访问。

      该数据库对所有用户免费开放,数据库连接信息为 postgres://postgres:postgres_root_local@47.74.20.164:31000/custom-db-connection,你可以创建测试用户,或者使用列表中的示例用户。

      配置好 Connection URI 之后点击保存,在点击最下方的 调试 按钮。

      在弹出的表单中填写示例用户池用户列表中的某个用户:

      点击测试按钮,你应该可以看到相关成功提示。

      上一篇: 自定义数据库 下一篇: 惰性迁移用户到 Authing

      本文是否有解决您的问题?

      如果遇到其他问题,你可以在 authing-chat/community 联系我们。

      • 开启自定义数据库连接
      • 选择场景
      • 配置数据库连接信息
      • 编写数据库操作脚本
      • 测试数据库脚本

      用户身份管理

      集成第三方登录
      手机号闪验 (opens new window)
      通用登录表单组件
      自定义认证流程

      企业内部管理

      单点登录
      多因素认证
      权限管理

      开发者

      开发文档
      框架集成
      博客 (opens new window)
      GitHub (opens new window)
      社区用户中心 (opens new window)

      公司

      服务状态
      15559944612
      sales@authing.cn
      北京市朝阳区北辰世纪中心 B 座 16 层(总)
      成都市高新区天府五街 200 号 1 号楼 B 区 4 楼 406 室(分)

      京ICP备19051205号

      beian京公网安备 11010802035968号

      © 北京蒸汽记忆科技有限公司