API 令牌

未匹配的标注

API 防护使用数据库支持的 opaque access token 来验证用户请求。在创建应由第三方客户端访问的 API 时,或为任何其他不支持 cookie 的系统创建 API 时,你可能希望使用 API 防护。

令牌存储

API 令牌保护允许你将令牌存储在 SQL 数据库中或将它们存储在 Redis 中。两种存储选项都有自己的用例。

SQL存储

SQL 存储方法适用于 API 令牌不是主要身份验证模式的情况。例如:你可能希望允许应用程序的用户创建个人访问令牌(就像 GitHub 所做的那样)并使用它来验证 API 请求。

在此场景中,你不会批量生成太多令牌,而且大多数令牌将永久存在。

令牌的配置在保护配置对象下的 config/auth.ts 文件中进行管理。

{
  api: {
    driver: 'oat',
    provider: {
      driver: 'lucid',
      identifierKey: 'id',
      uids: ['email'],
      model: () => import('App/Models/User'),
    },
    // highlight-start
    tokenProvider: {
      type: 'api',
      driver: 'database',
      table: 'api_tokens',
      foreignKey: 'user_id',
    },
    // highlight-end
  }
}

类型

type 属性保存你正在生成的令牌的类型。当你使用多个 API 令牌保护时,请确保给它一个唯一的名称。

唯一的名称确保为同一用户生成令牌的两个守卫没有重叠或任何冲突。


驱动程序

驱动程序的名称。将令牌存储在 SQL 表中时,它始终是 database


用于存储令牌的数据库表。在初始设置过程中,AdonisJS 将为令牌表创建迁移文件。但是,你也可以手动创建迁移并从 stub file


外键

建立用户和令牌之间关系的外键。稍后,你还可以列出给定用户的所有令牌。


Redis 存储

当 API 令牌是主要的身份验证模式时,redis 存储是合适的。例如:你使用基于令牌的身份验证对来自移动应用程序的请求进行身份验证。

在这种情况下,你还希望令牌在给定的时间段后过期,并且 redis 可以自动从其存储中清除过期的令牌。

令牌的配置在保护配置对象下的 config/auth.ts 文件中进行管理。

{
  api: {
    driver: 'oat',
    provider: {
      driver: 'lucid',
      identifierKey: 'id',
      uids: ['email'],
      model: () => import('App/Models/User'),
    },
    // highlight-start
    tokenProvider: {
      type: 'api',
      driver: 'redis',
      redisConnection: 'local',
      foreignKey: 'user_id',
    },
    // highlight-end
  }
}

类型

type 属性保存你正在生成的令牌的类型。当你使用多个 API 令牌保护时,请确保给它一个唯一的名称。

唯一的名称确保为同一用户生成令牌的两个守卫没有重叠或任何冲突。


driver

driver 的名称。在redis数据库中存储令牌时,始终是 redis


redisConnection

config/redis.ts 文件中定义的连接的引用。请务必阅读 redis 指南 进行初始设置。


foreignKey

建立用户和令牌之间关系的外键。


生成令牌

你可以使用 auth.generateauth.attempt 方法为用户生成 API 令牌。auth.attempt 方法从数据库中查找用户并验证他们的密码。

  • 如果用户凭据正确,它将在内部调用 auth.generate 方法并返回令牌。
  • 否则会引发 InvalidCredentialsException
import Route from '@ioc:Adonis/Core/Route'

Route.post('login', async ({ auth, request, response }) => {
  const email = request.input('email')
  const password = request.input('password')

  try {
    const token = await auth.use('api').attempt(email, password)
    return token
  } catch {
    return response.unauthorized('Invalid credentials')
  }
})

你可以手动处理异常并返回响应,或者让异常自行处理并使用 [内容协商](github.com/adonisjs/auth/blob/deve.... ts#L87-L105)。


auth.generate

如果 auth.attempt 查找策略不适合你的用例,那么你可以手动查找用户,验证他们的密码并调用 auth.generate 方法为他们生成令牌.

注意:
auth.login 方法是 auth.generate 方法的别名。

import User from 'App/Models/User'
import Route from '@ioc:Adonis/Core/Route'
import Hash from '@ioc:Adonis/Core/Hash'

Route.post('login', async ({ auth, request, response }) => {
  const email = request.input('email')
  const password = request.input('password')

  // 手动查找用户
  const user = await User
    .query()
    .where('email', email)
    .where('tenant_id', getTenantIdFromSomewhere)
    .whereNull('is_deleted')
    .firstOrFail()

  // 验证密码
  if (!(await Hash.verify(user.password, password))) {
    return response.unauthorized('Invalid credentials')
  }

  // 生成令牌
  const token = await auth.use('api').generate(user)
})

管理令牌到期

你还可以在生成令牌时定义令牌的到期时间。

await auth.use('api').attempt(email, password, {
  expiresIn: '7days'
})

await auth.use('api').generate(user, {
  expiresIn: '30mins'
})

redis驱动会自动删除过期的token。但是,对于 SQL 存储,你可以编写一个自定义脚本并删除 expires_at 时间戳小于今天的令牌。

令牌属性

以下是使用auth.generate 方法生成的令牌对象的属性列表。

type

令牌始终设置为 'bearer'


user

为其生成令牌的用户。用户的值依赖于 guard 使用的底层用户辅助器。
The user for which the token was generated. The value of the user relies on the underlying user provider used by the guard.


expiresAt

luxon Datetime 的一个实例,表示令牌到期的静态时间。仅在已明确定义令牌到期时才存在。


expiresIn

令牌将过期的时间(以秒为单位)。它是一个静态值,不会随着时间的推移而改变。


meta

随令牌附加的任何元数据。你可以在生成令牌时在选项对象中定义元数据。

注意:
底层存储驱动程序会将元数据持久化到数据库中。如果是 SQL,请确保还创建了所需的列。

await auth.use('api').attempt(email, password, {
  ip_address: '192.168.1.0'
})

name

与令牌关联的名称。当你允许应用程序的用户生成个人访问令牌(就像 GitHub 所做的那样)并给他们一个好记的名字时,这通常很有帮助。

name 属性仅在在生成令牌时定义它时才存在。

await auth.use('api').attempt(email, password, {
  name: 'For the CLI app'
})

token

生成的令牌的值。你必须与客户端共享此值,并且客户端必须安全地存储它。

你以后无法访问此值,因为存储在数据库中的值是令牌的哈希值,无法转换为真实值。


tokenHash

存储在数据库中的值。确保永远不要与客户端共享该值。

auth.authenticate 请求期间,我们会将客户端提供的值与 tokenHash 进行比较。


toJSON

将令牌转换为你可以发回以响应请求的对象。toJSON 方法包含以下属性。

{
  type: 'bearer',
  token: 'the-token-value',
  expires_at: '2021-04-28T17:43:37.235+05:30'
  expires_in: 604800
}

验证后续请求

客户端收到 API 令牌后,必须在 Authorization 标头下的每个 HTTP 请求中将其发回。标头的格式必须如下:

Authorization = Bearer TOKEN_VALUE

你可以使用 auth.authenticate 方法验证令牌是否有效。如果令牌无效或数据库中不存在用户,则会引发 AuthenticationException

否则,你可以使用 auth.user 属性访问登录用户。

import Route from '@ioc:Adonis/Core/Route'

Route.get('dashboard', async ({ auth }) => {
  await auth.use('api').authenticate()
  console.log(auth.use('api').user!)
})

在每条路由中手动调用此方法是不切实际的,因此你可以使用存储在 ./app/Middleware/Auth.ts 文件中的 auth 中间件。

[了解更多关于 auth 中间件的信息 →](https://learnku.com/docs/adonisjs/5.x/auth-middleware/13113)

撤销令牌

在注销阶段,你可以通过从数据库中删除令牌来撤销令牌。令牌必须在 Authorization 标头下再次发送。

auth.revoke 方法将从数据库中删除当前请求期间发送的令牌。

import Route from '@ioc:Adonis/Core/Route'

Route.post('/logout', async ({ auth, response }) => {
  await auth.use('api').revoke()
  return {
    revoked: true
  }
})

其他方法/属性

以下是 api 守卫可用的方法/属性列表。

isLoggedIn

查找用户是否已登录。在调用 auth.generate 方法之后或通过 auth.authenticate 检查时,该值为true

await auth.use('api').authenticate()
auth.use('api').isLoggedIn // true
await auth.use('api').attempt(email, password)
auth.use('api').isLoggedIn // true

isGuest

查找用户是否为访客(表示未登录)。该值始终与 isLoggedIn 标志相反。


isAuthenticated

查找当前请求是否通过了身份验证检查。此标志与 isLoggedIn 标志不同,并且在 auth.login 调用期间未设置为 true。

await auth.use('api').authenticate()
auth.use('api').isAuthenticated // true
await auth.use('api').attempt(email, password)
auth.use('api').isAuthenticated // false

isLoggedOut

查找令牌是否在当前请求期间被撤销。在调用 auth.revoke 方法后,该值将是 true

await auth.use('api').revoke()
auth.use('api').isLoggedOut

authenticationAttempted

查找是否已尝试对当前请求进行身份验证。当你调用 auth.authenticate 方法时,该值设置为 true

auth.use('api').authenticationAttempted // false

await auth.use('api').authenticate()
auth.use('api').authenticationAttempted // true

provider

对守卫使用的底层用户提供程序的引用。


tokenProvider

对守卫使用的底层令牌提供程序的引用。


verifyCredentials

一种验证用户凭据的方法。auth.attempt 方法在后台使用此方法。当凭据无效时会引发 InvalidCredentialsException

try {
  await auth.use('api').verifyCredentials(email, password)
} catch (error) {
  console.log(error)
}

check

该方法与 auth.authenticate 方法相同。但是,当请求未经过身份验证时,它不会引发任何异常。将其视为检查令牌是否对当前请求有效的可选尝试。

await auth.use('api').check()

if (auth.use('api').isLoggedIn) {
}

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/adonisjs/5.x/au...

译文地址:https://learnku.com/docs/adonisjs/5.x/au...

上一篇 下一篇
贡献者:2
讨论数量: 0
发起讨论 只看当前版本


暂无话题~