带有新身份验证方法的 Symfony 简单登录表单不起作用
Posted
技术标签:
【中文标题】带有新身份验证方法的 Symfony 简单登录表单不起作用【英文标题】:Symfony simple Login form with the new authentication method not working 【发布时间】:2021-10-20 12:47:26 【问题描述】:我在设置一个新的 symfony 应用程序时遇到问题,我确信它与新的基于 Authenticator 的安全系统有关。
-
我安装了一个全新的 symfony 应用程序,版本 5.3.6。
安装了安全包
composer require symfony/security-bundle
(https://symfony.com/doc/current/security.html)。
按照那里的所有步骤操作。
之后我想构建一个简单的
遵循本指南的登录表单:
https://symfony.com/doc/current/security/form_login_setup.html。一世
执行了命令php bin/console make:auth
,它生成了所有
文件并像往常一样更新了我的security.yml。在这里我注意到
该命令没有生成 Guard 身份验证器(因为我
了解其已过时),但新
一个(https://symfony.com/doc/current/security/authenticator_manager.html)。
之后我进入我的 /login 页面,输入凭据并提交
表格。页面重新加载,什么也没有。没有错误信息,我是
仍未通过身份验证。我没有做任何额外的步骤,因为它
应该按原样工作吗?好吧,至少是旧的 Guard 身份验证
就这样工作。然而,这个新的身份验证系统似乎
不行。我错过了什么吗?
我的文件:
LoginFormAuthenticator.php
class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
use TargetPathTrait;
public const LOGIN_ROUTE = 'app_login';
private UrlGeneratorInterface $urlGenerator;
public function __construct(UrlGeneratorInterface $urlGenerator)
$this->urlGenerator = $urlGenerator;
public function authenticate(Request $request): PassportInterface
$email = $request->request->get('email', '');
$request->getSession()->set(Security::LAST_USERNAME, $email);
return new Passport(
new UserBadge($email),
new PasswordCredentials($request->request->get('password', '')),
[
new CsrfTokenBadge('authenticate', $request->get('_csrf_token')),
]
);
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName))
return new RedirectResponse($targetPath);
// For example:
return new RedirectResponse($this->urlGenerator->generate('dashboard'));
protected function getLoginUrl(Request $request): string
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
SecurityController.php
class SecurityController extends AbstractController
/**
* @Route("/login", name="app_login")
*/
public function login(AuthenticationUtils $authenticationUtils): Response
// if ($this->getUser())
// return $this->redirectToRoute('target_path');
//
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
/**
* @Route("/logout", name="app_logout")
*/
public function logout()
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
security.yml
security:
# https://symfony.com/doc/current/security/experimental_authenticators.html
enable_authenticator_manager: true
# https://symfony.com/doc/current/security.html#c-hashing-passwords
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
App\Entity\User:
algorithm: auto
# 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\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: app_user_provider
custom_authenticator: App\Security\LoginFormAuthenticator
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
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# 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: ^/profile, roles: ROLE_USER
【问题讨论】:
您发布的内容看起来不错。我假设您确实按照您发布的代码所示调整了 onAuthenticationSuccess。尝试注释掉管理员访问控制行,以确保它没有搞砸。 @Cerad 问题是,onAuthenticationSuccess 甚至没有被调用。更重要的是,甚至没有调用 authenticate() 方法。我在 authenticate 方法中添加了die;
,提交了表单并重新加载了页面,这意味着我什至没有到达 authenticate() 方法......
您似乎定义了 app_login ,这意味着 support() 应该可以工作。即使发生了某种 csrf 废话,它仍然应该到达身份验证方法。你说这是一个新的应用程序,所以不应该有任何其他听众拦截东西。您的成功路线被命名为仪表板,但我假设您没有加载任何管理类型的包?开发服务器的控制台窗口中是否有任何有用的消息?
@Cerad 我检查了 supports() 方法并发现了问题......虽然也许你可以帮助我解决它。由于我将 wamp 用于开发目的,getLoginUrl() 方法返回我的完整路径:/workspace/public/login
,但 getPathInfo() 只是 /login
,因此 support() 方法总是返回 false
... 无论如何我可以处理吗?编辑:我覆盖了supports() 方法并将getPathInfo() 更改为getRequestUri() 并修复了它......最后......我会写下答案。谢谢!
我认为您可能需要一个 htaccess 文件来摆脱 /workspace/public。可能会在其他地方搞砸你。更好的是,只需使用 Symfony 开发服务器。这就是它的用途。
【参考方案1】:
在 AbstractLoginFormAuthenticator 类的 supports() 方法中发现了问题。该方法检查 getLoginUrl() 方法是否返回完整的 URI,而 getPathInfo() 在 URI 之后给出额外的路径信息。我不得不用 getRequestUri() 覆盖supports() 方法和thange getPathInfo() 并且它起作用了。
public function supports(Request $request): bool
return $request->isMethod('POST') && $this->getLoginUrl($request) === $request->getRequestUri();
【讨论】:
如果您使用反向代理路径 (public_url//prefix => internal_url) 并实施trusted_headers + x-forwarded-prefix,这似乎也会发生。以上是关于带有新身份验证方法的 Symfony 简单登录表单不起作用的主要内容,如果未能解决你的问题,请参考以下文章