用户和权限 Users & Permissions

使用基于 JWT 的完整身份验证流程保护您的 API,并管理用户组之间的权限。

此插件提供基于 JSON Web Tokens (JWT) 的完整身份验证流程,以保护您的 API。它还提供了访问控制列表 (ACL) 策略,使您能够管理用户组之间的权限。

要访问插件管理面板,请单击 Strapi 应用程序仪表板左侧菜单中的 设置 链接,在 用户和权限插件 部分下,您将找到用于管理 角色提供商电子邮件模板高级设置 的部分。

概念

安装此插件后,它会在您的应用程序上添加一个访问层。 该插件使用 JWTs 来验证用户。您的 JWT 包含您的用户 ID,该 ID 与您的用户所在的组匹配,并用于确定是否允许访问路由。

每次发送 API 请求时,服务器都会检查是否存在“授权”标头,并验证发出请求的用户是否有权访问资源。

管理角色权限

公共角色

这是服务器收到没有“授权”标头的请求时使用的默认角色。授予此角色的任何权限(即可访问端点)都可供任何人访问。

当您希望前端应用程序无需用户身份验证和授权即可访问所有内容时,通常选择“find”/“findOne”端点。

经过身份验证的角色

如果未提供任何角色,则这是在创建时为每个新用户提供的默认角色。在此角色中,您可以定义用户可以访问的路由。

权限管理

通过单击角色名称,您可以查看应用程序中可用的所有功能(这些功能与显示的特定路由相关)。

如果您检查函数名称,它将使您正在编辑的当前角色可以访问此路由。在右侧边栏中,您可以看到与此功能相关的 URL。

更新默认角色

当您创建没有角色的用户时,或者如果您使用 /api/auth/local/register 路由,则会将 authenticated 角色赋予用户。

要更改默认角色,请转到 Advanced settings 选项卡并更新 Default role for authenticated users 选项。

身份验证

登录

提交用户的标识符和密码凭据以进行身份验证。身份验证成功后,响应数据将包含用户的信息以及身份验证令牌。

本地

identifier 参数可以是 emailusername

js
Axios
js
import axios from 'axios'

// Request API.
axios
  .post('http://localhost:1337/api/auth/local', {
    identifier: 'user@strapi.io',
    password: 'strapiPassword',
  })
  .then((response) => {
    // Handle success.
    console.log('Well done!')
    console.log('User profile', response.data.user)
    console.log('User token', response.data.jwt)
  })
  .catch((error) => {
    // Handle error.
    console.log('An error occurred:', error.response)
  })

令牌使用

然后可以使用 jwt 发出权限受限的 API 请求。要以用户身份发出 API 请求,请将 JWT 放入 GET 请求的 Authorization 标头中。

默认情况下,任何没有令牌的请求都将假定 public 角色权限。在管理仪表板中修改每个用户角色的权限。

身份验证失败将返回 401(未授权) 错误。

使用

token 变量是登录或注册时收到的 data.jwt

js
import axios from 'axios'

const token = 'YOUR_TOKEN_HERE'

// Request API.
axios
  .get('http://localhost:1337/posts', {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  })
  .then((response) => {
    // Handle success.
    console.log('Data: ', response.data)
  })
  .catch((error) => {
    // Handle error.
    console.log('An error occurred:', error.response)
  })

JWT 配置

您可以使用 插件配置文件 配置 JWT 生成。

Strapi 使用 jsonwebtoken 生成 JWT。

可用选项:

  • jwtSecret:用于创建新 JWT 的随机字符串,通常使用 JWT_SECRET 环境变量 设置。
  • jwt.expiresIn:以秒为单位表示或描述时间跨度的字符串。

例如:60、“45m”、“10h”、“2 days”、“7d”、“2y”。数值被解释为秒数。如果使用字符串,请确保提供时间单位(分钟、小时、天、年等),否则默认使用毫秒单位(“120”等于“120ms”)。

js
./config/plugins.js
js
module.exports = ({ env }) => ({
  // ...
  'users-permissions': {
    config: {
      jwt: {
        expiresIn: '7d',
      },
    },
  },
  // ...
})

出于安全考虑,不建议将 JWT 有效期设置为超过 30 天。

注册

配置

如果您在用户模型中添加了任何需要在注册时接受的附加字段,则需要将它们添加到 register 配置选项中允许的字段列表中,否则将不被接受。

例如,如果您添加了一个名为“昵称”的字段,希望在用户注册时从 API 中接受该字段:

js
./config/plugins.js
js
module.exports = ({ env }) => ({
  // ...
  'users-permissions': {
    config: {
      register: {
        allowedFields: ['nickname'],
      },
    },
  },
  // ...
})

用法

在数据库中创建一个新用户,其默认角色为“已注册”。

js
import axios from 'axios'

// 请求 API。
// 在此处添加您自己的代码以自定义或限制公众注册新用户的方式。
axios
  .post('http://localhost:1337/api/auth/local/register', {
    username: 'Strapi user',
    email: 'user@strapi.io',
    password: 'strapiPassword',
  })
  .then((response) => {
    // Handle success.
    console.log('Well done!')
    console.log('User profile', response.data.user)
    console.log('User token', response.data.jwt)
  })
  .catch((error) => {
    // Handle error.
    console.log('An error occurred:', error.response)
  })

提供程序

GrantPurest 允许您使用 OAuth 和 OAuth2 提供程序在您的应用程序中启用身份验证。

为了更好地理解,请查看以下登录流程描述。 该示例使用 github 作为提供程序,但它对其他提供程序的工作方式相同。

了解登录流程

假设:

  • Strapi 的后端位于:strapi.website.com,并且
  • 您的应用前端位于:website.com
  1. 用户进入您的前端应用(https://website.com)并点击您的按钮“连接 Github”。
  2. 前端将选项卡重定向到后端 URL:https://strapi.website.com/api/connect/github
  3. 后端将选项卡重定向到用户登录的 GitHub 登录页面。
  4. 完成后,Github 将选项卡重定向到后端 URL:https://strapi.website.com/api/connect/github/callback?code=abcdef
  5. 后端使用给定的 code 从 Github 获取 access_token,该令牌可在一段时间内用于向 Github 发出授权请求以获取用户信息。
  6. 然后,后端使用参数 access_token 将选项卡重定向到您选择的 URL(例如:http://website.com/connect/github/redirect?access_token=eyfvg)。
  7. 前端 (http://website.com/connect/github/redirect) 使用 https://strapi.website.com/api/auth/github/callback?access_token=eyfvg 调用后端,后端返回带有 jwt 的 Strapi 用户配置文件。 (在后台,后端向 Github 询问用户的配置文件,并对 Github 用户的电子邮件地址和 Strapi 用户的电子邮件地址进行匹配)。
  8. 前端现在拥有用户的 jwt,这意味着用户已连接,前端可以向后端发出经过身份验证的请求!

处理此流程的前端应用程序示例可在此处找到:react login 示例应用程序

设置服务器 URL

在设置提供程序之前,您必须在 server.js 中指定后端的绝对 URL。

示例 - config/server.js

js
config/server.js
js
module.exports = ({ env }) => ({
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),
  url: env('', 'http://localhost:1337'),
})

稍后您将此 URL 提供给您的提供商。\ 对于开发,一些提供商接受使用本地主机 URL,但许多提供商不接受。在这种情况下,我们建议使用 ngrok (ngrok http 1337),它将从它创建的 URL 到您的本地主机 URL 建立代理隧道,例如:url: env('', 'https://5299e8514242.ngrok.io'),

设置提供商 - 示例

我们决定为每个提供商展示一个示例,而不是通用解释。

在以下示例中,前端应用程序将是 react login 示例应用程序
它(前端应用程序)将在 http://localhost:3000 上运行。
Strapi(后端)将在http://localhost:1337上运行。

GitHub
详情
Facebook
详情
Google
详情
AWS Cognito
详情
Twitter
详情
Discord
详情
Twitch
详情
Instagram
详情
VK
详情
LinkedIn
详情

您的配置已完成。 启动后端和 react login 示例应用程序,转到 http://localhost:3000 并尝试连接到您配置的提供程序。

设置前端

配置完 strapi 和提供程序后,您必须在前端应用程序中:

  • 创建一个链接到 GET STRAPI_BACKEND_URL/api/connect/${provider} 的按钮(例如:https://strapi.mywebsite/api/connect/github)。
  • 创建一个前端路由,如 FRONTEND_URL/connect/${provider}/redirect,该路由必须处理 access_token 参数,并且必须使用 access_token 参数请求 STRAPI_BACKEND_URL/api/auth/${provider}/callback
    JSON 请求响应将是 { "jwt": "...", "user": {...} }

现在您可以发出经过身份验证的请求。更多信息请见此处:token usage

::card{type=tip} 故障排除

  • 错误 429:很可能是因为您的登录流程陷入了循环。要向后端发出新请求,您需要等待几分钟或重新启动后端。
  • Grant:缺少会话或提供程序配置错误:可能由多种原因造成。
  • 无法构建重定向网址:确保您已在 config/server.js 中设置了后端网址:设置服务器网址
  • 会话/cookie/缓存问题:您可以在私人标签中重试。
  • ngrok 域名使用不正确:检查您的网址,确保您使用的是 ngrok 网址,而不是 http://localhost:1337。不要忘记检查示例应用程序中 src/config.js 中设置的后端网址。
  • 您无法访问管理面板:很可能是因为您使用 ngrok 网址设置的后端网址构建了它,并且您停止/重新启动了 ngrok。您需要用新的 ngrok 网址替换后端网址,然后再次运行 yarn buildnpm run build。 ::

重置密码

仅供使用电子邮件提供商注册的用户使用。

忘记和重置流程
详情
更改密码流程
详情

电子邮件验证

在生产中,请确保设置了 url 配置属性。否则验证链接将重定向到 localhost。有关配置的更多信息,请参见此处

注册后,如果您将 启用电子邮件确认 设置为 开启,则用户将通过电子邮件收到确认链接。用户必须单击它才能验证其注册。

确认链接示例:https://yourwebsite.com/api/auth/email-confirmation?confirmation=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MywiaWF0IjoxNTk0OTgxMTE3LCJleHAiOjE1OTc1NzMxMTd9.0WeB-mvuguMyr4eY8CypTZDkunR--vZYzZH6h6sChFg

如有必要,您可以通过以下请求重新发送确认电子邮件:

js
import axios from 'axios'

// Request API.
axios
  .post(`http://localhost:1337/api/auth/send-email-confirmation`, {
    email: 'user@strapi.io', // user's email
  })
  .then((response) => {
    console.log('Your user received an email')
  })
  .catch((error) => {
    console.error('An error occurred:', error.response)
  })

Strapi 上下文中的用户对象

user 对象可用于成功验证的请求。

经过验证的 user 对象是 ctx.state 的一个属性。

js
create: async (ctx) => {
  const { id } = ctx.state.user

  const depositObj = {
    ...ctx.request.body,
    depositor: id,
  }

  const data = await strapi.services.deposit.add(depositObj)

  // Send 201 `created`
  ctx.created(data)
}

模板电子邮件

默认情况下,此插件附带两个模板:重置密码和电子邮件地址确认。模板使用 Lodash 的 template() 方法来填充变量。

您可以在管理面板中的 插件 > 角色和权限 > 电子邮件模板 选项卡下更新这些模板。

重置密码

  • USER (对象)
  • username
  • email
  • TOKEN 对应于生成的用于重置密码的令牌。
  • URL 是用户在电子邮件中单击后将被重定向到的链接。
  • SERVER_URL 是绝对服务器 URL(在服务器配置中配置)。

电子邮件地址确认

  • USER (对象)
  • username
  • email
  • CODE 对应于生成的用于确认用户电子邮件的 CODE。
  • URL 是确认代码的 Strapi 后端 URL(默认情况下为 /auth/email-confirmation)。
  • SERVER_URL 是绝对服务器 URL(在服务器配置中配置)。

安全配置

JWT 可以被验证和信任,因为信息是经过数字签名的。要签署令牌,需要一个 secret。默认情况下,Strapi 会生成并将其存储在 ./extensions/users-permissions/config/jwt.js 中。

这在开发过程中很有用,但出于安全原因,建议在部署到生产时通过环境变量 JWT_SECRET 设置自定义令牌。

默认情况下,您可以设置 JWT_SECRET 环境变量,它将用作机密。如果您想使用其他变量,您可以更新配置文件。

js
Javascript
js
// ./extensions/users-permissions/config/jwt.js
module.exports = {
  jwtSecret: process.env.SOME_ENV_VAR,
}

您可以在此处了解有关配置的更多信息。

创建自定义回调验证器

默认情况下,Strapi SSO 仅重定向到与配置中的 URL 完全相等的重定向 URL:

Users & Permissions configuration

如果您需要配置自定义处理程序来接受其他 URL,您可以在 plugins.js 中为 users-permissions 插件创建一个回调 validate 函数。

/config/plugins.js|ts
jsx
// ... other plugins configuration ...
// Users & Permissions configuration
const usersPermissions = {
  enabled: true,
  config: {
    callback: {
      validate: (cbUrl, options) => {
        // cbUrl 是要求 Strapi 将从提供商收到的身份验证信息重定向到

        // 在这种情况下,我们只会验证是否使用基本 URL,你应该始终包含尾部斜杠,尽管在实际使用中你还应该包含完整路径
        if (cbUrl.startsWith('https://myproxy.mysite.com/')
          || cbUrl.startsWith('https://mysite.com/')) {
          return
        }

        // 请注意,你必须抛出一个错误来使验证失败,否则将不检查返回值
        throw new Error('Invalid callback url')
      },
    },
  },
}