步骤 15: 保护管理后台
保护管理后台¶
管理后台的界面应该只能让信任的人来访问。可以用 Symfony 的 Security 组件把网站的这个区域保护起来。
和 Twig 一样,Security 组件作为传递性依赖已经安装好了。我们来将它显式地加入项目的 composer.json
文件:
1 | $ symfony composer req security
|
定义 User 实体¶
虽然参会人员不能在网站上创建他们自己的账号,但我们要为管理员开发一套完备的认证系统。所以我们只会有一个用户,那就是网站管理员。
第一步是定义 User
实体类。为了避免混淆,我们把它命名为 Admin
。
为了把 Admin
实体整合到 Security 组件的认证系统,该实体需要满足一些条件。比如,它需要一个 password
属性。
使用量身定做的 make:user
命令来创建 Admin
实体,而不是用传统的 make:entity
命令:
1 | $ symfony console make:user Admin
|
回答命令行交互模式下的问题:我们想要用 Doctrine 来存储管理员(选择 yes
),使用 username
属性作为管理员的独一显示名,每个用户需要有密码(选择 yes
)。
命令生成的类文件里包含的方法有 getRoles()
、eraseCredentials()
以及其它一些,这些方法都会被 Symfony 的认证系统调用。
如果你要 Admin
类里增加更多属性,请用 make:entity
。
让我们增加一个 __toString()
方法,因为 EasyAdmin 会用到它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | --- a/src/Entity/Admin.php
+++ b/src/Entity/Admin.php
@@ -75,6 +75,11 @@ class Admin implements UserInterface
return $this;
}
+ public function __toString(): string
+ {
+ return $this->username;
+ }
+
/**
* @see UserInterface
*/
|
这个命令除了生成 Admin
实体,它也更新了安全配置文件,将这个实体类接入到认证系统:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | --- a/config/packages/security.yaml
+++ b/config/packages/security.yaml
@@ -1,7 +1,15 @@
security:
+ encoders:
+ App\Entity\Admin:
+ algorithm: auto
+
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
- in_memory: { memory: null }
+ # used to reload user from session & other features (e.g. switch_user)
+ app_user_provider:
+ entity:
+ class: App\Entity\Admin
+ property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
对密码明文进行加密有多种可能的算法,我们让 Symfony 来选择最优的算法(这个选择会随时间改变)。
是时候生成一个数据库结构迁移文件,并且更新数据库结构了:
1 2 | $ symfony console make:migration
$ symfony console doctrine:migrations:migrate -n
|
为管理员生成一个密码¶
我们不会去开发一个专用的系统用于管理员的账号创建。再说一遍,我们这里只会有一个管理员。它的账号名就叫 admin
,并且我们需要对密码进行加密。
选一个你想要的密码,然后运行以下的命令来生成一个加密后的密码:
1 | $ symfony console security:encode-password
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | Symfony Password Encoder Utility
================================
Type in your password to be encoded:
>
------------------ ---------------------------------------------------------------------------------------------------
Key Value
------------------ ---------------------------------------------------------------------------------------------------
Encoder used Symfony\Component\Security\Core\Encoder\MigratingPasswordEncoder
Encoded password $argon2id$v=19$m=65536,t=4,p=1$BQG+jovPcunctc30xG5PxQ$TiGbx451NKdo+g9vLtfkMy4KjASKSOcnNxjij4gTX1s
------------------ ---------------------------------------------------------------------------------------------------
! [NOTE] Self-salting encoder used: the encoder generated its own built-in salt.
[OK] Password encoding succeeded
|
创建一个管理员¶
用 SQL 语句插入一个管理员用户:
1 2 3 | $ symfony run psql -c "INSERT INTO admin (id, username, roles, password) \
VALUES (nextval('admin_id_seq'), 'admin', '[\"ROLE_ADMIN\"]', \
'\$argon2id\$v=19\$m=65536,t=4,p=1\$BQG+jovPcunctc30xG5PxQ\$TiGbx451NKdo+g9vLtfkMy4KjASKSOcnNxjij4gTX1s')"
|
请注意密码那一列里,我们对 $
符号进行了转义;对每个 $
都进行转义!
配置认证系统¶
现在我们既然有了管理员用户,就可以去保护起后台了。Symfony 支持几种认证策略。让我们用经典而且流行的 表单认证系统。
运行 make:auth
命令来更新安全方面的配置,生成一个登录页模板,并且创建一个 认证器:
1 | $ symfony console make:auth
|
选择 1
来生成一个登录表单认证器,将这个认证器的类命名为 AppAuthenticator
,将控制器类命名为 SecurityController
,并且生成一个 /logout
路径(选择 yes
)。
这个命令会更新安全配置,将生成的类接入认证系统:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | --- a/config/packages/security.yaml
+++ b/config/packages/security.yaml
@@ -16,6 +16,13 @@ security:
security: false
main:
anonymous: lazy
+ guard:
+ authenticators:
+ - App\Security\AppAuthenticator
+ logout:
+ path: app_logout
+ # where to redirect after logout
+ # target: app_any_route
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
|
按照命令输出的提示,我们需要在 onAuthenticationSuccess()
方法中设置一个定制路径,它是用户登录成功后要跳转的路径:
1 2 3 4 5 6 7 8 9 10 11 12 | --- a/src/Security/AppAuthenticator.php
+++ b/src/Security/AppAuthenticator.php
@@ -96,8 +96,7 @@ class AppAuthenticator extends AbstractFormLoginAuthenticator implements Passwor
return new RedirectResponse($targetPath);
}
- // For example : return new RedirectResponse($this->urlGenerator->generate('some_route'));
- throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
+ return new RedirectResponse($this->urlGenerator->generate('easyadmin'));
}
protected function getLoginUrl()
|
小技巧
我是如何知道 EasyAdmin 路由的名字叫 easyadmin
的?其实我并不知道,但是我运行了下面这个命令,它会告诉我路由名和路径之间的关联:
1 | $ symfony console debug:router
|
增加授权访问控制的规则¶
一个安全系统由两部分组成:认证 和 授权。当创建一个管理员时,我们给了它 ROLE_ADMIN
的角色。让我们来限定 /admin
路径下的区域只能允许拥有该角色的用户才能访问,我们是通过在 access_control
下增加一条规则来实现的:
1 2 3 4 5 6 7 8 9 | --- a/config/packages/security.yaml
+++ b/config/packages/security.yaml
@@ -34,5 +34,5 @@ security:
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- # - { path: ^/admin, roles: ROLE_ADMIN }
+ - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
|
access_control
下的规则通过正则表达式来限制访问。当用户尝试访问的 URL 以 /admin
开头时,安全系统会检查这个登录的用户是否有 ROLE_ADMIN
这个角色。
通过登录表单认证¶
现在如果你试着进入后台,你会被重定向到登录页面,并被要求录入账户名和密码:

账户名是 admin
,密码就是你之前编码的明文密码。如果你不做修改地复制了我的 SQL 命令,那么密码就是 admin
。
注意,EasyAdmin 自动识别出了 Symfony 的认证系统:

试着点击“退出”链接。完成了!后台被充分地保护起来了。
注解
如果想要一个功能完备的表单认证系统,去看一下 make:registration-form
命令。
- « Previous 步骤 14: 利用表单接收反馈
- Next » 步骤 16: 用 API 防止垃圾信息
This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.