# 角色 & 权限
此插件提供了一种通过基于 JWT 的完整身份验证过程来保护你的 API 的方法。此插件还附带了 ACL 策略,允许你管理用户组之间的权限。
要访问插件管理面板,请单击左侧菜单中的 Settings 链接,然后所有内容都将位于 USERS & PERMISSIONS PLUGIN 部分下。
# 概念
安装此插件后,它会在你的应用程序上添加一个访问层。
该插件使用 jwt token
(opens new window) 效验用户。
每次发送 API 请求时,服务器便会检查 Authorization
协议头是否存在,并验证发出请求的用户是否有权访问资源。
为此,你的 JWT 包含你的用户 ID,并且我们能够匹配你的用户所在的组,并在最后知道该组是否允许访问路由。
# 管理角色权限
# 公共角色
当发送没带有 Authorization
协议头的请求时,将使用此角色。
如果您允许此角色的某些权限,则每个人都可以访问您选择的选项。
当您希望前端应用程序在不开发用户身份验证和授权的情况下访问所有内容时,选择 find
/ findOne
选项是常见的做法。
# 经过身份验证的角色
在创建用户时未提供任何角色,则为每个新用户提供的默认角色(Authenticated role)。在此角色中,您将能够定义用户可以访问的路由。
# 权限管理
通过单击 Role 名称,您将能够看到应用程序中可用的所有功能(并且这些功数与特定路由相关)如果勾选功能名称,则会使您正在编辑的当前角色可以访问此路由。在右侧边栏上,您将能够看到与此功能相关的 URL。
# 更新默认角色
当你使用 /api/auth/local/register
路由创建一个无角色的用户时,将会为该用户分配 authenticated
角色。
要修改默认角色,请单击 高级设置
(Advanced settings
) 标签并且更新 经过身份验证的用户的默认角色
(Default role for authenticated users
) 选项。
# 认证
# Token 用法
jwt token 可用于发出受权限限制的 API 请求。要以用户身份发出 API 请求,请将 jwt token 放入 GET 请求的 Authorization
协议头中。默认情况下,没有令牌的请求将采取 公共
(public
) 角色权限。在管理仪表板中修改每个用户角色的权限。身份验证失败将返回 401(未经授权)错误。
# 用法
token
变量是在登录或注册时从响应中data.jwt
获取。
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 生成。 我们使用 jsonwebtoken (opens new window) 来生成 JWT。
可用选项:
jwtSecret
: 随机字符串生成 JWT 签名。通常使用JWT_SECRET
环境变量 设置。jwt.expiresIn
: 以秒或描述时间跨度/毫秒的字符串表示。
例如: 60, "45m", "10h", "2 days", "7d", "2y". 数值被解释为秒计数。如果使用字符串,请确保提供正确时间单位 (minutes, hours, days, years, etc),否则默认使用毫秒单位( "120" 等于 "120ms")。
️❗️ WARNING
由于大量的安全问题,绝对不建议
将JWT到期时间设置为30天以上。
Setting JWT expiry for more than 30 days is absolutely not recommended due to massive security concerns.
# 注册
在数据库中创建一个默认角色为“已注册”的新用户。
# 用法
import axios from 'axios';
// Request API.
// Add your own code here to customize or restrict how the public can register new users.
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);
});
# 登录
提交用户的标识符和密码凭据进行身份验证。身份验证成功后,返回的响应数据将包含用户的信息以及 jwt 身份验证令牌。
# 本地
identifier
参数可以是 email 或 username。
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);
});
# 提供者
借助 Grant (opens new window) 和 Purest (opens new window), 您可以轻松地使用 OAuth 和 OAuth2 提供程序在应用程序中启用身份验证。
为了更好地理解,您可能会发现登录流程的说明如下。为了简化解释,我们使用 github
作为提供者,但它对其他提供者的工作方式相同。
# 了解登录流程
假设 strapi 的后端位于:strapi.website.com。 假设 strapi 的前端端位于:website.com。
- 用户进入您的前端应用程序 (
https://website.com
) 然后单击connect with Github
按钮。 - 前端将标签页重定向到后端 URL:
https://strapi.website.com/api/connect/github
. - 后端将标签页重定向到用户登录的 GitHub 登录页面。
- 完成后,Github会将标签页重定向到后端 URL:
https://strapi.website.com/api/connect/github/callback?code=abcdef
. - 后端使用给定的
code
从 Github 获取access_token
,该access_token
可以在一段时间内用于向 Github 发出授权请求以获取用户信息(例如用户的电子邮件)。 - 然后,后端将选项卡重定向到您选择的URL,参数为
access_token
(例如:http://website.com/connect/github/redirect?access_token=eyfvg
) - 前端 使用 (
http://website.com/connect/github/redirect
) 调用后端的https://strapi.website.com/api/auth/github/callback?access_token=eyfvg
返回带有jwt
的 strapi 用户配置文件。
(在后台,后端要求Github提供用户的个人资料,并在 Github 用户的电子邮件地址和 Strapi 用户的电子邮件地址上进行匹配) - 前端现在拥有用户的
jwt
,这意味着用户已连接,前端可以向后端发出经过身份验证的请求!
可在此处找到处理此流的前端应用的示例: react login example app (opens new window).
# 设置服务器网址
在设置提供程序之前,您需要在 server.js
中指定后端的绝对 URL。
example - config/server.js
💡 提示
稍后,您将此 URL 提供给您的提供商。
对于开发,一些提供商接受使用本地主机网址,但许多提供商不接受。 在这种情况下,我们建议使用 ngrok (opens new window) (ngrok http 1337
) 这将使代理隧道从它创建的 URL 到您的本地主机 URL(例: url: env('', 'https://5299e8514242.ngrok.io'),
)。
# 设置提供程序 - 示例
为了更好地理解,我们决定为每个提供者展示一个示例,而不是一般的解释。
在以下示例中,前端应用程序将是 react login example app (opens new window)。
前端应用程序将在 http://localhost:3000
上运行。
Strapi 后端将在 http://localhost:1337
上运行。
Your configuration is done.
Launch the backend and the react login example app (opens new window), go to http://localhost:3000
and try to connect to the provider your configured. It should work 🎉
# What you have to do in your frontend
Once you have configured strapi and the provider, in your frontend app you have to :
- Create a button that links to
GET STRAPI_BACKEND_URL/api/connect/${provider}
(ex:https://strapi.mywebsite/api/connect/github
). - Create a frontend route like
FRONTEND_URL/connect/${provider}/redirect
that have to handle theaccess_token
param and that have to requestSTRAPI_BACKEND_URL/auth/${provider}/callback
with theaccess_token
param.
The JSON request response will be{ "jwt": "...", "user": {...} }
.
Now you can make authenticated requests 🎉 More info here: token usage.
✋ Troubleshooting
- Error 429: It's most likely because your login flow fell into a loop. To make new requests to the backend, you need to wait a few minutes or restart the backend.
- Grant: missing session or misconfigured provider: It may be due to many things.
- The redirect url can't be built: Make sure you have set the backend url in
config/server.js
: Setting up the server url - A session/cookie/cache problem: You can try again in a private tab.
- The incorrect use of a domain with ngrok: Check your urls and make sure that you use the ngrok url instead of
http://localhost:1337
. Don't forget to check the backend url set in the example app atsrc/config.js
.
- The redirect url can't be built: Make sure you have set the backend url in
- You can't access your admin panel: It's most likely because you built it with the backend url set with a ngrok url and you stopped/restarted ngrok. You need to replace the backend url with the new ngrok url and run
yarn build
ornpm run build
again.
# 重置密码
只能用于使用电子邮件提供商注册的用户。
# 邮件验证
✏️ NOTE
在生产环境中,请确保设置了 url
配置属性。否则,验证链接将重定向到 localhost
。有关配置的更多信息点 这里。
注册后,如果将 启用电子邮件确认 设置为 ON,则用户将通过电子邮件收到确认链接。用户必须单击它以验证他/她的注册。
确认链接的示例: https://yourwebsite.com/api/auth/email-confirmation?confirmation=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MywiaWF0IjoxNTk0OTgxMTE3LCJleHAiOjE1OTc1NzMxMTd9.0WeB-mvuguMyr4eY8CypTZDkunR--vZYzZH6h6sChFg
如果需要,您可以通过提出以下请求来重新发送确认电子邮件。
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
的属性。
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);
};
# 添加新的提供程序(添加到项目中)
✋ CAUTION
本文档不是 Strapi v4 的最新文档,是一项正在进行的工作。同时欢迎贡献 (opens new window)。
Grant (opens new window) supplies configuration for a number of commonly used OAuth providers. Custom (opens new window) providers are also supported.
You can view and try out the 200+ supported providers here: OAuth Playground (opens new window).
# Prepare your files
To add a new provider on Strapi, you will need to perform changes onto the following files:
extensions/users-permissions/services/Providers.js
extensions/users-permissions/config/functions/bootstrap.js
If these files don't exist you will need to copy from your node_modules
or the Strapi mono-repo. You can see plugin extensions for more information on how it works.
We will go step by step.
# Configure your Provider Request
Configure the new provider in the Provider.js
file at the getProfile
function.
The getProfile
takes three params:
- provider: The name of the used provider as a string.
- query: The query is the result of the provider callback.
- callback: The callback function who will continue the internal Strapi login logic.
Here is an example that uses the discord
provider.
# Configure your oauth generic information
case 'discord': {
const discord = new Purest({
provider: 'discord',
config: {
'discord': {
'https://discordapp.com/api/': {
'__domain': {
'auth': {
'auth': {'bearer': '[0]'}
}
},
'{endpoint}': {
'__path': {
'alias': '__default'
}
}
}
}
}
});
}
This code creates a Purest
object that gives us a generic way to interact with the provider's REST API.
For more specs on using the Purest
module, please refer to the Official Purest Documentation (opens new window)
You may also want to take a look onto the numerous already made configurations here (opens new window).
# Retrieve your user's information:
For our discord provider it will look like:
discord.query().get('users/@me').auth(access_token).request((err, res, body) => {
if (err) {
callback(err);
} else {
// Combine username and discriminator because discord username is not unique
const username = `${body.username}#${body.discriminator}`;
callback(null, {
username,
email: body.email
});
}
});
break;
}
Here is the next part of our switch. Now that we have properly configured our provider, we want to use it to retrieve user information.
Here you see the real power of purest
, you can simply make a get request on the desired URL, using the access_token
from the query
parameter to authenticate.
That way, you should be able to retrieve the user info you need.
Now, you can simply call the callback
function with the username and email of your user. That way, Strapi will be able
to retrieve your user from the database and log you in.
# Configure the new provider model onto database
Now, we need to configure our 'model' for our new provider. That way, our settings can be stored in the database, and managed from the admin panel.
Open the file packages/strapi-plugin-users-permissions/config/functions/bootstrap.js
Add the fields your provider needs into the grantConfig
object.
For our discord provider it will look like:
discord: {
enabled: false, // make this provider disabled by default
icon: 'comments', // The icon to use on the UI
key: '', // our provider app id (leave it blank, you will fill it with the Content Manager)
secret: '', // our provider secret key (leave it blank, you will fill it with the Content Manager)
callback: '/auth/discord/callback', // the callback endpoint of our provider
scope: [ // the scope that we need from our user to retrieve information
'identify',
'email'
]
},
# Templating emails
By default, this plugin comes with only two templates (reset password and email address confirmation) at the moment. More templates will come later. The templates use Lodash's template() method to populate the variables.
You can update these templates under Plugins > Roles & Permissions > Email Templates tab in the admin panel.
# Reset Password
USER
(object)username
email
TOKEN
corresponds to the token generated to be able to reset the password.URL
is the link where the user will be redirected after clicking on it in the email.
# Email address confirmation
USER
(object)username
email
CODE
corresponds to the CODE generated to be able confirm the user email.URL
is the Strapi backend URL that confirms the code (by default/auth/email-confirmation
).
# Security configuration
JWT tokens can be verified and trusted because the information is digitally signed. To sign a token a secret is required. By default Strapi generates one that is stored in ./extensions/users-permissions/config/jwt.js
. This is useful during development but for security reasons it is recommended to set a custom token via an environment variable JWT_SECRET
when deploying to production.
By default you can set a JWT_SECRET
environment variable and it will be used as secret. If you want to use another variable you can update the configuration file.
💡 TIP
You can learn more on configuration in the documentation here.