slashid / symfony
SlashID集成包,适用于Symfony。
Requires
- php: ^8.1
- slashid/php: 1.0.2
- symfony/yaml: ^7.0
Requires (Dev)
- doctrine/orm: ^3.1
- friendsofphp/php-cs-fixer: ^3.54
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^11.1
- symfony/config: ^7
- symfony/dependency-injection: ^7
- symfony/http-kernel: ^7
- symfony/routing: ^7.0
- symfony/security-bundle: ^7
- symfony/security-core: ^7
- symfony/security-http: ^7
- symfony/translation: ^7.0
- twig/twig: ^3
This package is not auto-updated.
Last update: 2024-09-24 22:35:32 UTC
README
安装
- 使用composer安装包含Symfony的SlashID包
composer require slashid/symfony
- 编辑环境文件
.env
,在文件末尾添加以下变量SLASHID_ENVIRONMENT
,可以是sandbox
或production
SLASHID_ORGANIZATION_ID
,您的组织ID。您可以在SlashID控制台中找到它(生产环境:https://console.slashid.dev/,沙盒环境:https://console.sandbox.slashid.dev/),在“设置”选项卡中,页面顶部。SLASHID_API_KEY
,您的组织API密钥。您也可以在SlashID控制台中找到它,在“设置”选项卡中,页面底部。
# .env
SLASHID_ENVIRONMENT=sandbox
SLASHID_ORGANIZATION_ID=412edb57-ae26-f2aa-9999-770021ed52d1
SLASHID_API_KEY=z0dlY-nluiq8mcvm8YTolSkJV6e9
- 运行Symfony命令以发布包资源
php bin/console assets:install
- 编辑
config/routes.yaml
,在文件末尾添加以下内容
# config/routes.yaml _slashid_symfony_bundle: resource: '@slashid/config/routes.yaml'
- 编辑
config/packages/security.yaml
,添加以下SlashID引用
# config/packages/security.yaml security: providers: slashid: id: slashid.user_provider firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: lazy: true provider: slashid custom_authenticators: - slashid.authenticator logout: path: /logout
您已经准备好了!现在访问网站中的/login
并享受SlashID的新登录吧 :)
无状态登录
如果您的Symfony安装作为API后端(例如React应用程序)运行,您可能需要配置无状态登录。要这样做,只需将stateless: true
添加到防火墙配置中,例如
# config/packages/security.yaml security: # ................ firewalls: api: pattern: ^/api provider: slashid custom_authenticators: - slashid.authenticator stateless: true main: lazy: true provider: slashid custom_authenticators: - slashid.authenticator
在上面的示例中,任何对/api/****
的请求将通过发送Authorization: Bearer <<TOKEN>>
头中的SlashID令牌进行登录。所有其他路由将使用cookie进行登录。
⚠️ 注意!如果您不添加stateless: true
,则带有Authorization: Bearer <<TOKEN>>
头的请求将创建cookie登录。
配置
如果您想通过在Symfony安装中创建config/packages/slashid.yaml
文件来自定义SlashID集成,例如
# config/packages/slashid.yaml slashid: login_form: configuration: analytics-enabled: false theme-props: theme: dark factors: - { "method": "webauthn" } - { "method": "email_link" } - { "method": "password" } css_override: --sid-color-primary: "#f00" --sid-color-primary-hover: "#900" route_after_login: 'dashboard'
这些是配置选项
登录表单配置
登录表单是SlashID的React SDK的捆绑版本。因此,组件中所有选项在这里都是可用的,只需注意您必须将camelCase
转换为kebab-case
(见以下示例)。
例如,要使用深色主题,请这样做
# config/packages/slashid.yaml slashid: login_form: configuration: theme-props: theme: dark
如果您想启用密码登录并禁用电子邮件链接登录,请这样做
# config/packages/slashid.yaml slashid: login_form: configuration: factors: - { "method": "webauthn" } - { "method": "password" }
您还可以覆盖由React SDK提供的任何CSS变量。例如,要使登录按钮为红色,悬停时颜色更深,可以这样做
# config/packages/slashid.yaml slashid: login_form: css_override: --sid-color-primary: "#f00" --sid-color-primary-hover: "#900"
组
基于组的路由访问
SlashID中的组作为Symfony角色公开。因此,例如,如果您有一个名为“编辑器”的组,则用户将具有ROLE_EDITOR
角色。
您可以通过编辑security.yaml
来保护基于SlashID组的路由,如下所示
# config/packages/security.yaml security: # .......... access_control: - { path: "^/editor", roles: ROLE_EDITOR } - { path: ^/admin, roles: ROLE_ADMIN }
⚠️ 注意!所有组名称都将大写,因此名为“编辑器”、“editor”和“EDITOR”的组都将转录为ROLE_EDITOR
。
在自定义代码中检查组
如果您想检查自定义代码中用户的组,您可以使用\SlashId\Symfony\SlashIdUser
类的任何与组相关的函数,例如
// src/Controller/MyCustomController.php namespace App\Controller; <?php namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; class TestController extends AbstractController { #[Route('/my-test-route')] public function testRoute(): Response { /** @var \SlashId\Symfony\SlashIdUser */ $user = $this->getUser(); if ($user->hasGroup('Editor')) { // Do things that only an "Editor" user can do. } if ($user->hasAnyGroup(['Admin', 'Editor', 'Reviewer'])) { // Do things that someone in the group "Admin", OR in the group // "Editor", OR in the group "Reviewer" can do. } if ($user->hasAllGroups(['Admin', 'Editor'])) { // Do things that only someone that is in *both* "Admin" and // "Editor" groups can do. } // Shows the user groups as a list of strings. dd($user->getGroups()); } }
在Twig中检查组
您还可以使用hasGroup
/ hasAnyGroup
/ hasAllGroups
方法来构建Twig模板,根据用户所属的组显示不同内容。
{# templates/test.html.twig #} {% if app.user %} <p>You are logged in</p> {% if app.user.hasGroup('Editor') %} <p>Information Editors can access.</p> {% endif %} {% if app.user.hasGroup('Admin') %} <p>Information Admins can access.</p> {% endif %} {% if app.user.hasAnyGroup(['Admin', 'Editor']) %} <p>Information both Editors and Admins can access.</p> {% endif %} {{ dump(app.user.getGroups()) }} {% else %} <p>You are NOT logged in</p> {% endif %}
Webhooks
CLI webhook命令
要使用webhooks,您需要首先在SlashID中注册您的URL。webhooks通过API管理,但此包提供了三个CLI命令来帮助您管理它们。
如何注册webhooks
要为当前网站注册新的webhook,请使用以下命令。您需要为它定义一个唯一的名称,在此示例中,我们使用my_webhook
。
php bin/console slashid:webhook:register my_webhook
默认情况下,webhook注册了以下触发器:PersonDeleted_v1
、PersonLoggedOut_v1
和PasswordChanged_v1
。您可以指定要注册哪些触发器,使用空格分隔触发器列表
php bin/console slashid:webhook:register my_webhook PasswordChanged_v1 VirtualPageLoaded_v1 AuthenticationFailed_v1
您可以多次运行slashid:webhook:register
,如果已在该URL注册了webhook,则它将被更新,并且触发器列表将被覆盖。
如何在本地测试webhooks
您可以使用类似ngrok的工具在本地开发环境中测试webhooks,然后使用--base-url
选项注册具有代理的webhook。
例如,如果您在8080端口上运行Symfony,可以使用以下命令使用ngrok代理您的本地环境:
ngrok http 8000
ngrok命令行将显示有关代理的数据,例如:
Forwarding https://2f45-2804-14c-483-983f-b323-32f2-4714-1609.ngrok-free.app -> https://:8000
然后,您可以使用以下命令将Web服务注册到代理URL:
php bin/console slashid:webhook:register proxied_webhook PasswordChanged_v1 --base-url=https://2f45-2804-14c-483-983f-b323-32f2-4714-1609.ngrok-free.app
如何为其他应用程序注册webhooks
您可以使用CLI命令将webhooks注册到任何任意URL
php bin/console slashid:webhook:register proxied_webhook PasswordChanged_v1 --webhook-url=https://someotherapplication.example.com/some-arbitrary-url
如何查看现有webhooks
您可以使用以下命令查看您SlashID组织中注册的所有webhooks:
php bin/console slashid:webhook:list
如何删除webhook
您可以通过ID删除webhook。
php bin/console slashid:webhook:delete 065e5237-c1c4-7a96-ab00-783ef0cbd002
要了解webhook ID,请使用slashid:webhook:list
命令。
监听事件
任何接收到的webhook都将作为Symfony事件提供给开发者。
要在您的Symfony应用程序中监听webhook事件,在您的应用程序的src/EventListener
文件夹中创建一个类。在下面的示例中,我们将其命名为WebhookEventListener
,但您可以随意命名。
// src/EventListener/WebhookEventListener.php namespace App\EventListener; use SlashId\Symfony\Event\WebhookEvent; use Symfony\Component\EventDispatcher\Attribute\AsEventListener; #[AsEventListener] class WebhookEventListener { public function __invoke(WebhookEvent $event): void { print_r([ $event->getEventName(), $event->getEventId(), $event->getTriggerContent(), ]); } }
监听器将接收类为\SlashId\Symfony\Event\WebhookEvent
的事件。它有三个方法,您可以使用它们提取有关webhook调用的信息
$event->getEventName()
将返回触发器名称,例如AuthenticationFailed_v1
,即在发送到webhook的JSON中的->trigger_content->event_metadata->event_name
。$event->getEventId()
将返回事件ID,例如68a850ca-b2ee-46ce-8592-410813037739
,即在发送到webhook的JSON中的->trigger_content->event_metadata->event_id
。$event->getTriggerContent()
将返回webhook调用的全部内容,即在发送到webhook的JSON中的->trigger_content
。
重写路由
默认情况下,Symfony包公开了三个路由
/login
- 登录表单/login/callback
- 背景中调用的路由以完成登录过程/slashid/webhook
- webhook监听器
在某些情况下,您可能不想公开webhook或登录表单,甚至可能要更改这些路由的URL。为了完成此操作,首先从config/routes.yaml
中删除_slashid_symfony_bundle:
行,然后手动添加路由
# config/routes.yaml # Remove the two lines below. #_slashid_symfony_bundle: # resource: '@slashid/config/routes.yaml' slashid.login: path: /custon-path-for-login controller: SlashId\Symfony\Controller\LoginController::login slashid.login.callback: path: /custon-path-for-login/callback controller: SlashId\Symfony\Controller\LoginController::loginCallback # In this example we don't enable the webhook listener. #slashid.webhook: # path: /slashid/webhook # controller: SlashId\Symfony\Controller\WebhookController::webhook
⚠️ 注意:您必须保留路由名称:slashid.login
、slashid.login.callback
和slashid.webhook
。
用户迁移
如果您在现有的Symfony网站上安装SlashID,您可能已经有一个您希望迁移到SlashID数据库的用户基础。这可以通过两个迁移命令轻松实现。
首先,您必须运行命令php bin/console slashid:import:create-script
。它将询问您安装中的用户类,通常是\App\Models\User
。
$ php bin/console slashid:import:create-script
Please inform the class of the user model [\App\Entity\User]:
>
[OK] The Slash ID migration script has been created at
/var/www/symfony/migrations/slashid/user-migration.php. Please open the file and modify it
according to the instructions in it.
将在migrations/slashid/user-migration.php
中创建一个脚本(可以通过修改migration_script_folder
选项来自定义文件夹目标)。它看起来是这样的:
<?php use App\Entity\User; use Doctrine\ORM\EntityManagerInterface; use SlashId\Php\PersonInterface; use SlashId\Symfony\SlashIdUser; return static function (EntityManagerInterface $entityManager): array { /** @var array<User> */ $doctrineUsers = $entityManager->getRepository(User::class)->findAll(); $slashIdUsers = []; foreach ($doctrineUsers as $doctrineUser) { $roles = $doctrineUser->getRoles(); unset($roles[array_search('ROLE_USER', $roles)]); // Converts "ROLE_ADMIN" into "Admin". $roles = array_map(fn (string $role) => ucwords(strtolower(str_replace('_', ' ', str_replace('ROLE_', '', $role)))), $roles); $slashIdUser = new SlashIdUser(); $slashIdUser ->addEmailAddress($doctrineUser->getEmail()) ->setLegacyPasswordToMigrate($doctrineUser->getPassword()) // Uncomment if you want to set the phone number. // ->addPhoneNumber($doctrineUser->getPhoneNumber()) ->setGroups($roles) // Uncomment if you want to specify a region for the user. // ->setRegion('us-iowa') ->setBucketAttributes(PersonInterface::BUCKET_ORGANIZATION_END_USER_NO_ACCESS, [ // List the user attributes you want to migrate, grouped by bucket. 'old_id' => $doctrineUser->getUserIdentifier(), // 'first_name' => $doctrineUser->getFirstName(), // 'last_name' => $doctrineUser->getLastName(), ]) ; $slashIdUsers[] = $slashIdUser; } return $slashIdUsers; };
您必须修改user-migration.php
以根据需要建模要迁移的数据,特别是添加您想要的属性。脚本必须返回一个包含您想要批量导入到SlashID的所有用户的\SlashId\Symfony\SlashIdUser
数组。
根据您的需求修改脚本后,运行php bin/console slashid:import:run
,例如:
$ php bin/console slashid:import:run
+------------------------+---------------+--------+-------+--------+-------------------------------------------------------------------------------------------------------------------------------+
| Emails | Phone numbers | Region | Roles | Groups | Attributes |
+------------------------+---------------+--------+-------+--------+-------------------------------------------------------------------------------------------------------------------------------+
| rattazzi@example.com | | | | | {"end_user_no_access":{"old_id":1,"firstname":"Urbano","email_verified_at":null,"lastname":"Rattazzi","username":"rattazzi"}} |
| nitti@example.com | | | | | {"end_user_no_access":{"old_id":2,"firstname":"Francesco","email_verified_at":null,"lastname":"Nitti","username":"nitti"}} |
| cavour@example.com | | | | | {"end_user_no_access":{"old_id":3,"firstname":"Camillo","email_verified_at":null,"lastname":"Cavour","username":"cavour"}} |
+------------------------+---------------+--------+-------+--------+-------------------------------------------------------------------------------------------------------------------------------+
Do you want to proceed with importing 3 users? (yes/no) [no]:
> yes
2 successfully imported users.
1 users failed to import. Check the file /var/www/symfony/migrations/slashid/migration-failed-202403271142.csv for errors.
迁移过程中出现的任何错误将以CSV格式输出。检查CSV以修复错误并再次运行。
覆盖登录表单
Twig模板以及如何在布局中插入表单
登录表单在Twig模板templates/login/login.html.twig
中渲染,该模板继承自base.html.twig
,因此登录表单被网站的默认布局所包围。
如果您想更改模板,将文件vendor/slashid/symfony/templates/login/login.html.twig
复制到templates/bundles/slashid/login/login.html.twig
并按需修改。
使用自定义JavaScript
Laravel包附带了一个SlashID React SDK的包和一小段位于vendor/slashid/symfony/public/slashid.symfony-web-login.js
中的JavaScript粘合代码。
您可能想覆盖捆绑的React SDK以编译您的React登录表单实现。如果是这样,将config/packages/slashid.yaml
中的选项slashid.login_form.override_bundled_javascript
更改为true
以防止加载捆绑的React SDK。
或者,您可能想覆盖粘合代码,在登录后包含自定义操作。如果是这样,将选项slashid.login_form.override_javascript_glue
更改为true
以防止加载粘合代码。
在这两种情况下,您都需要自行加载您的自定义代码。