步骤 15: 保护管理后台

    和 Twig 一样,Security 组件作为传递性依赖已经安装好了。我们来将它显式地加入项目的 文件:

    虽然参会人员不能在网站上创建他们自己的账号,但我们要为管理员开发一套完备的认证系统。所以我们只会有一个用户,那就是网站管理员。

    第一步是定义 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. --- a/src/Entity/Admin.php
    2. +++ b/src/Entity/Admin.php
    3. @@ -75,6 +75,11 @@ class Admin implements UserInterface
    4. return $this;
    5. }
    6. + public function __toString(): string
    7. + {
    8. + return $this->username;
    9. + }
    10. +
    11. /**
    12. * @see UserInterface
    13. */

    这个命令除了生成 Admin 实体,它也更新了安全配置文件,将这个实体类接入到认证系统:

    1. --- a/config/packages/security.yaml
    2. +++ b/config/packages/security.yaml
    3. @@ -1,7 +1,15 @@
    4. security:
    5. + encoders:
    6. + App\Entity\Admin:
    7. + algorithm: auto
    8. +
    9. - in_memory: { memory: null }
    10. + # used to reload user from session & other features (e.g. switch_user)
    11. + app_user_provider:
    12. + entity:
    13. + class: App\Entity\Admin
    14. + property: username
    15. firewalls:
    16. dev:
    17. pattern: ^/(_(profiler|wdt)|css|images|js)/

    对密码明文进行加密有多种可能的算法,我们让 Symfony 来选择最优的算法(这个选择会随时间改变)。

    为管理员生成一个密码

    我们不会去开发一个专用的系统用于管理员的账号创建。再说一遍,我们这里只会有一个管理员。它的账号名就叫 admin,并且我们需要对密码进行加密。

    选一个你想要的密码,然后运行以下的命令来生成一个加密后的密码:

    1. $ symfony console security:encode-password
    1. Symfony Password Encoder Utility
    2. ================================
    3. Type in your password to be encoded:
    4. >
    5. ------------------ ---------------------------------------------------------------------------------------------------
    6. Key Value
    7. ------------------ ---------------------------------------------------------------------------------------------------
    8. Encoder used Symfony\Component\Security\Core\Encoder\MigratingPasswordEncoder
    9. Encoded password $argon2id$v=19$m=65536,t=4,p=1$BQG+jovPcunctc30xG5PxQ$TiGbx451NKdo+g9vLtfkMy4KjASKSOcnNxjij4gTX1s
    10. ------------------ ---------------------------------------------------------------------------------------------------
    11. ! [NOTE] Self-salting encoder used: the encoder generated its own built-in salt.
    12. [OK] Password encoding succeeded

    用 SQL 语句插入一个管理员用户:

    1. $ symfony run psql -c "INSERT INTO admin (id, username, roles, password) \
    2. VALUES (nextval('admin_id_seq'), 'admin', '[\"ROLE_ADMIN\"]', \
    3. '\$argon2id\$v=19\$m=65536,t=4,p=1\$BQG+jovPcunctc30xG5PxQ\$TiGbx451NKdo+g9vLtfkMy4KjASKSOcnNxjij4gTX1s')"

    请注意密码那一列里,我们对 $ 符号进行了转义;对每个 $ 都进行转义!

    配置认证系统

    现在我们既然有了管理员用户,就可以去保护起后台了。Symfony 支持几种认证策略。让我们用经典而且流行的 表单认证系统

    运行 make:auth 命令来更新安全方面的配置,生成一个登录页模板,并且创建一个 认证器

    选择 1 来生成一个登录表单认证器,将这个认证器的类命名为 AppAuthenticator,将控制器类命名为 SecurityController,并且生成一个 /logout 路径(选择 )。

    这个命令会更新安全配置,将生成的类接入认证系统:

    1. +++ b/config/packages/security.yaml
    2. @@ -16,6 +16,13 @@ security:
    3. security: false
    4. main:
    5. anonymous: lazy
    6. + guard:
    7. + authenticators:
    8. + - App\Security\AppAuthenticator
    9. + logout:
    10. + path: app_logout
    11. + # where to redirect after logout
    12. + # target: app_any_route
    13. # activate different ways to authenticate
    14. # https://symfony.com/doc/current/security.html#firewalls-authentication

    按照命令输出的提示,我们需要在 onAuthenticationSuccess() 方法中设置一个定制路径,它是用户登录成功后要跳转的路径:

    1. --- a/src/Security/AppAuthenticator.php
    2. +++ b/src/Security/AppAuthenticator.php
    3. @@ -96,8 +96,7 @@ class AppAuthenticator extends AbstractFormLoginAuthenticator implements Passwor
    4. return new RedirectResponse($targetPath);
    5. }
    6. - // For example : return new RedirectResponse($this->urlGenerator->generate('some_route'));
    7. - throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
    8. + return new RedirectResponse($this->urlGenerator->generate('admin'));
    9. }
    10. protected function getLoginUrl()

    小技巧

    我是如何记住 EasyAdmin 路由的名字叫 admin 的(正如在 App\Controller\Admin\DashboardController 里所配置的)?其实我并不记得。你可以看一下控制器文件,但你可以运行下面这个命令,它会展示路由名和路径之间的关联:

    1. $ symfony console debug:router

    access_control 下的规则通过正则表达式来限制访问。当用户尝试访问的 URL 以 /admin 开头时,安全系统会检查这个登录的用户是否有 ROLE_ADMIN 这个角色。

    通过登录表单认证

    现在如果你试着进入后台,你会被重定向到登录页面,并被要求录入账户名和密码:

    账户名是 admin,密码就是你之前编码的明文密码。如果你不做修改地复制了我的 SQL 命令,那么密码就是 。

    注意,EasyAdmin 自动识别出了 Symfony 的认证系统:

    步骤 15: 保护管理后台 - 图2

    试着点击“退出”链接。完成了!后台被充分地保护起来了。

    注解

    如果想要一个功能完备的表单认证系统,去看一下 make:registration-form 命令。

    深入学习