php - Symfony4 多重保护验证器重定向问题
问题描述
对于我的 Symfony4 应用程序,我想:
- 1个中台防火墙
- 1 个用于后台的防火墙。
为每个人配备一个专用的 Guard 身份验证器听起来不错(尤其是登录后重定向,获取登录页面......)。我曾经make
创建过保护文件样板,最后得到了下面的文件。
虽然在中间办公室登录可以直接工作,但当我尝试去/admin
part时,我一直有一个重定向循环。
日志显示admin
防火墙在这里被正确捕获为好的防火墙,但是尽管我app_admin_login
为匿名访问设置了 OK,但它一直重定向到入口点,即admin/login
. 为什么这样?
我肯定想念这里的东西,谢谢你的启发。
[2019-02-13 11:46:39] request.INFO: Matched route "easyadmin". {"route":"easyadmin","route_parameters":{"_controller":"Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController::urlRedirectAction","path":"/admin/","permanent":true,"scheme":null,"httpPort":8900,"httpsPort":443,"_route":"easyadmin"},"request_uri":"http://localhost:8900/admin","method":"GET"} []
[2019-02-13 11:46:39] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"admin","authenticators":1} []
[2019-02-13 11:46:39] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"admin","authenticator":"App\\Security\\AdminLoginFormAuthenticator"} []
[2019-02-13 11:46:39] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"admin","authenticator":"App\\Security\\AdminLoginFormAuthenticator"} []
[2019-02-13 11:46:39] security.INFO: An AuthenticationException was thrown; redirecting to authentication entry point. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationCredentialsNotFoundException(code: 0): A Token was not found in the TokenStorage. at /var/www/html/vendor/symfony/security-http/Firewall/AccessListener.php:51)"} []
[2019-02-13 11:46:39] security.DEBUG: Calling Authentication entry point. [] []
[2019-02-13 11:46:39] request.INFO: Matched route "app_admin_login". {"route":"app_admin_login","route_parameters":{"_route":"app_admin_login","_controller":"App\\Controller\\SecurityController::adminLogin"},"request_uri":"http://localhost:8900/admin/login","method":"GET"} []
[2019-02-13 11:46:39] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"admin","authenticators":1} []
[2019-02-13 11:46:39] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"admin","authenticator":"App\\Security\\AdminLoginFormAuthenticator"} []
[2019-02-13 11:46:39] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"admin","authenticator":"App\\Security\\AdminLoginFormAuthenticator"} []
[2019-02-13 11:46:39] security.INFO: An AuthenticationException was thrown; redirecting to authentication entry point. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationCredentialsNotFoundException(code: 0): A Token was not found in the TokenStorage. at /var/www/html/vendor/symfony/security-http/Firewall/AccessListener.php:51)"} []
[2019-02-13 11:46:39] security.DEBUG: Calling Authentication entry point. [] []
[2019-02-13 11:46:39] request.INFO: Matched route "app_admin_login". {"route":"app_admin_login","route_parameters":{"_route":"app_admin_login","_controller":"App\\Controller\\SecurityController::adminLogin"},"request_uri":"http://localhost:8900/admin/login","method":"GET"} []
[2019-02-13 11:46:39] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"admin","authenticators":1} []
[2019-02-13 11:46:39] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"admin","authenticator":"App\\Security\\AdminLoginFormAuthenticator"} []
[2019-02-13 11:46:39] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"admin","authenticator":"App\\Security\\AdminLoginFormAuthenticator"} []
[2019-02-13 11:46:39] security.INFO: An AuthenticationException was thrown; redirecting to authentication entry point. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationCredentialsNotFoundException(code: 0): A Token was not found in the TokenStorage. at /var/www/html/vendor/symfony/security-http/Firewall/AccessListener.php:51)"} []
[2019-02-13 11:46:39] security.DEBUG: Calling Authentication entry point. [] []
... Until max loop iteration number reached
安全.yaml
security:
encoders:
App\Entity\Shop\User:
algorithm: argon2i
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\Shop\User
property: email
role_hierarchy:
ROLE_ADMIN: ROLE_USER
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
admin:
pattern: ^/admin
logout:
path: app_logout
guard:
authenticators:
- App\Security\AdminLoginFormAuthenticator
# logout_on_user_change: true
main:
pattern: ^/
anonymous: true
logout:
path: app_logout
guard:
authenticators:
- App\Security\LoginFormAuthenticator
# logout_on_user_change: true
# activate different ways to authenticate
# http_basic: true
# https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
# form_login: true
# https://symfony.com/doc/current/security/form_login_setup.html
# 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/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/, roles: ROLE_USER }
AdminLoginFormAuthenticator
class AdminLoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
use TargetPathTrait;
private $entityManager;
private $urlGenerator;
private $csrfTokenManager;
private $passwordEncoder;
private $request;
public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
{
$this->entityManager = $entityManager;
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
$this->passwordEncoder = $passwordEncoder;
}
public function supports(Request $request)
{
$this->request = $request;
return ('app_admin_login' === $request->attributes->get('_route'))
&& $request->isMethod('POST');
}
public function getCredentials(Request $request)
{
$credentials = [
'email' => $request->request->get('email'),
'password' => $request->request->get('password'),
'csrf_token' => $request->request->get('_csrf_token'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['email']
);
return $credentials;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
$user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('Email could not be found.');
}
return $user;
}
public function checkCredentials($credentials, UserInterface $user)
{
return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
$user = $token->getUser();
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
return new RedirectResponse($this->urlGenerator->generate('easyadmin'));
}
protected function getLoginUrl()
{
return $this->urlGenerator->generate('app_admin_login');
}
}
解决方案
感谢@dbrumann 为我指明了正确的方向,我设法解决了问题。这只是一种解决方法,因为它不解释重定向本身,而是避免它。
我重写了这个AbstratcFormLoginAuthenticator::start()
方法:
/**
* Overrided to avoid redirection loop
* @param Request $request
* @param AuthenticationException|null $authException
*
* @return RedirectResponse|Response
* @throws \Exception
*/
public function start(Request $request, AuthenticationException $authException = null)
{
$url = $this->getLoginUrl();
// If URL different than login one, redirect to login one
if($url !== $this->urlGenerator->generate($request->attributes->get('_route'))) {
return new RedirectResponse($url);
}
// Render login page as a subrequest for this call only
else {
$path['_controller'] = SecurityController::class . '::adminLogin';
$subRequest = $request->duplicate([], null, $path);
return $this->kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
}
}
重定向到admin/login
页面是有效的,admin
防火墙被调用,管理员保护响应和登录成功重定向在管理部分。
推荐阅读
- c - 混淆为什么程序输出不正确问题是检查整数是否是阿姆斯壮数
- html - 如何在 div 容器之外对齐我的图像?
- php - touch(): Utime failed: 不允许操作
- python - 如何让python在下载文件时点击弹出按钮自动打开文件?
- regex - 环顾内的多个正则表达式匹配
- python-3.x - 循环遍历 PuLP 嵌套变量以获取特定约束
- sql - 返回表数据类型的 T-SQL 函数。想遍历这张表
- javascript - vercel 上是否有“now dev”的替代命令?
- angularjs - AngularJS highcharts 点击事件
- java - 如何在终端上正确打印月历