身份验证

本指南介绍 AdonisJS 身份验证系统。你将学习:

  • 守卫(guards)和提供者(providers)如何协同工作来验证用户
  • 为你的应用类型选择哪个守卫
  • 如何安装和配置 auth 包
  • Initialize auth 中间件的作用

概述

AdonisJS 提供了一个健壮的身份验证系统,用于登录和验证不同应用类型中的用户,无论你是在构建服务端渲染应用、供 SPA 使用的 API,还是移动应用的后端。

身份验证包围绕两个核心概念构建:

  • 守卫(Guards) 是特定身份验证方法的端到端实现。例如,session 守卫通过 cookie 和会话来验证用户,而 access tokens 守卫则使用 bearer token 来验证请求。
  • 提供者(Providers) 负责从你的数据库中查找用户并验证 token。你可以使用内置的 Lucid 提供者,或者针对自定义数据源实现你自己的提供者。

AdonisJS 的安全原语旨在防范常见的漏洞。密码和 token 会被正确地进行哈希处理,并且实现会防范 计时攻击(timing attacks)会话固定攻击(session fixation attacks)

auth 包不包含的内容

auth 包专注于验证 HTTP 请求。以下功能不在其范围内:

  • 用户注册(表单、邮箱验证、账户激活)
  • 账户管理(密码找回、邮箱更新)
  • 授权和权限(请改用 Bouncer
Tip

在寻找完整的身份验证系统吗? AdonisJS Kit 提供了全栈组件,带有用于用户注册、邮箱验证、密码找回、个人资料管理等开箱即用的流程。

选择身份验证守卫

AdonisJS 内置了三个守卫。使用下表来确定哪个守卫适合你的应用。

应用类型推荐守卫原因
服务端渲染的 Web 应用Sessioncookie 与浏览器请求天然协作
同一域名下的 SPASessionapi.example.comexample.com 之间共享 cookie
不同域名下的 SPAAccess tokens跨源请求无法共享 cookie
移动应用Access tokens原生应用无法使用基于 cookie 的会话
第三方 API 访问Access tokens客户端需要可存储的长生命周期 token
快速原型开发Basic auth设置简单,无需数据库表

Session 守卫

session 守卫使用 @adonisjs/session 包来跟踪已登录用户。登录成功后,用户的标识符会存储在会话中,并向浏览器发送一个会话 cookie。后续请求会包含这个 cookie,使服务器能够恢复用户的已认证状态。

几十年来,会话和 cookie 一直是 Web 身份验证的标准。当你的客户端可以接受和发送 cookie 时,它们工作得很好,对于服务端渲染应用以及托管在与你的 API 相同顶级域名下的 SPA 来说正是这种情况。

另请参阅: Session 守卫文档

Access tokens 守卫

Access tokens 是在登录后发给用户的加密安全随机字符串。客户端存储该 token,并将其包含在后续请求的 Authorization 请求头中。AdonisJS 使用不透明的 access tokens(而非 JWT),它们以哈希形式存储在你的数据库中以供验证。

当你的客户端无法使用 cookie 时,请使用 access tokens:

  • 原生移动应用
  • 桌面应用
  • 与你的 API 不同域的 Web 应用
  • 需要编程式 API 访问的第三方集成

客户端应用负责安全地存储 token。Access tokens 代表用户对你的应用提供无限制的访问权限,因此泄露它们会带来安全风险。

另请参阅: Access tokens 守卫文档

Basic auth 守卫

basic auth 守卫实现了 HTTP 身份验证框架 。客户端在每个请求的 Authorization 请求头中发送 base64 编码的凭据。如果凭据无效,浏览器会显示一个原生的登录提示。

不建议在生产应用中使用基本身份验证,因为凭据会随着每个请求一起发送,并且用户体验仅限于浏览器内置的提示。但是,在开发的早期阶段或内部工具中,它会很有用。

另请参阅: Basic auth 守卫文档

选择用户提供者

用户提供者(user providers)在身份验证期间负责查找用户和验证 token。每种守卫类型对其提供者都有特定要求。

session 守卫提供者通过 ID(存储在会话中)查找用户。access tokens 守卫提供者还会根据数据库中的哈希值验证 token。AdonisJS 为所有内置守卫都提供了基于 Lucid 的提供者,它们使用你的 Lucid 模型来查询数据库。

安装

auth 包已通过 webapi 起步套件预先配置好。要将它添加到现有应用中,请基于你偏好的守卫运行以下命令之一:

# Session 守卫(推荐用于 Web 应用)
node ace add @adonisjs/auth --guard=session

# Access tokens 守卫(推荐用于 API)
node ace add @adonisjs/auth --guard=access_tokens

# Basic auth 守卫
node ace add @adonisjs/auth --guard=basic_auth
查看 add 命令执行的步骤
  1. 使用检测到的包管理器安装 @adonisjs/auth 包。

  2. adonisrc.ts 文件中注册以下服务提供者。

    {
      providers: [
        // ...其他 providers
        () => import('@adonisjs/auth/auth_provider')
      ]
    }
  1. start/kernel.ts 文件中创建并注册以下中间件。
    router.use([
      () => import('@adonisjs/auth/initialize_auth_middleware')
    ])
    router.named({
      auth: () => import('#middleware/auth_middleware'),
      // 仅在使用 session 守卫时注册
      guest: () => import('#middleware/guest_middleware')
    })
  1. app/models 中创建 User 模型。

  2. users 表创建数据库迁移。

  3. 根据所选守卫创建额外的迁移(例如,针对 access tokens 守卫的 auth_access_tokens)。

Initialize auth 中间件

在设置期间,@adonisjs/auth/initialize_auth_middleware 会被添加到你的应用中间件栈中。该中间件在每个请求上运行,并创建 Authenticator 类的实例,将其附加到 ctx.auth

initialize auth 中间件不会验证请求或保护路由。它唯一的职责是设置 authenticator 实例,使其在请求生命周期内可用。要保护路由,请使用 auth 中间件

如果你的应用使用 Edge 模板,authenticator 也可作为 auth 变量使用:

@if(auth.isAuthenticated)
  <p>Hello {{ auth.user.email }}</p>
@end

创建 users 表

add 命令会在 database/migrations 中为 users 表创建一个迁移。在运行迁移之前,你可以修改此文件以匹配你的应用需求。

database/migrations/TIMESTAMP_create_users_table.ts
import { BaseSchema } from '@adonisjs/lucid/schema'

export default class extends BaseSchema {
  protected tableName = 'users'

  async up() {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id').notNullable()
      table.string('full_name').nullable()
      table.string('email', 254).notNullable().unique()
      table.string('password').notNullable()

      table.timestamp('created_at').notNullable()
      table.timestamp('updated_at').nullable()
    })
  }

  async down() {
    this.schema.dropTable(this.tableName)
  }
}

修改迁移后,更新 app/models/user.ts 中的 User 模型以反映你添加、重命名或删除的任何列。

下一步

安装好 auth 包后,你就可以在应用中实现身份验证了: