如何创建和启用自定义用户检查器
在用户身份验证期间,可能需要额外的检查来验证已识别的用户是否允许登录。通过定义自定义用户检查器,你可以为每个防火墙定义应使用的检查器。
创建自定义用户检查器
用户检查器是必须实现 UserCheckerInterface 接口的类。此接口定义了两个方法,分别称为 checkPreAuth()
和 checkPostAuth()
,用于在用户身份验证之前和之后执行检查。如果一个或多个条件未满足,则抛出一个异常,该异常扩展了 AccountStatusException 类。考虑使用 CustomUserMessageAccountStatusException,它扩展了 AccountStatusException
,并允许自定义向用户显示错误消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
namespace App\Security;
use App\Entity\User as AppUser;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Exception\AccountExpiredException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class UserChecker implements UserCheckerInterface
{
public function checkPreAuth(UserInterface $user): void
{
if (!$user instanceof AppUser) {
return;
}
if ($user->isDeleted()) {
// the message passed to this exception is meant to be displayed to the user
throw new CustomUserMessageAccountStatusException('Your user account no longer exists.');
}
}
public function checkPostAuth(UserInterface $user, TokenInterface $token): void
{
if (!$user instanceof AppUser) {
return;
}
// user account is expired, the user may be notified
if ($user->isExpired()) {
throw new AccountExpiredException('...');
}
if (!\in_array('foo', $token->getRoleNames())) {
throw new AccessDeniedException('...');
}
}
}
7.2
checkPostAuth()
方法的 token
参数在 Symfony 7.2 中引入。
启用自定义用户检查器
接下来,确保你的用户检查器注册为服务。如果你正在使用 默认的 services.yaml 配置,则该服务将自动注册。
剩下要做的就是将检查器添加到所需的防火墙,其中值是你的用户检查器的服务 ID
1 2 3 4 5 6 7 8 9
# config/packages/security.yaml
# ...
security:
firewalls:
main:
pattern: ^/
user_checker: App\Security\UserChecker
# ...
使用多个用户检查器
应用程序通常具有多个身份验证入口点(例如传统的基于表单的登录和 API),这些入口点可能对每个入口点具有唯一的检查器规则,以及对所有入口点具有通用规则。为了允许在防火墙上使用多个用户检查器,为每个防火墙创建了 ChainUserChecker 类的服务。
要使用链式用户检查器,首先你需要使用 security.user_checker.<firewall>
标签标记你的用户检查器服务(其中 <firewall>
是你安全配置中防火墙的名称)。服务标签还支持 priority 属性,允许你定义调用用户检查器的顺序
1 2 3 4 5 6 7 8 9 10 11 12
# config/services.yaml
# ...
services:
App\Security\AccountEnabledUserChecker:
tags:
- { name: security.user_checker.api, priority: 10 }
- { name: security.user_checker.main, priority: 10 }
App\Security\APIAccessAllowedUserChecker:
tags:
- { name: security.user_checker.api, priority: 5 }
一旦你的检查器服务被标记,接下来你需要配置你的防火墙以使用 security.user_checker.chain.<firewall>
服务
1 2 3 4 5 6 7 8 9 10 11 12 13
# config/packages/security.yaml
# ...
security:
firewalls:
api:
pattern: ^/api
user_checker: security.user_checker.chain.api
# ...
main:
pattern: ^/
user_checker: security.user_checker.chain.main
# ...