身份验证

本指南介绍 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 与浏览器请求天然协作
同一域名下的 SPASession在 api.example.com 和 example.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 包已通过 web 和 api 起步套件预先配置好。要将它添加到现有应用中,请基于你偏好的守卫运行以下命令之一:

# 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 包后,你就可以在应用中实现身份验证了: