我已经在安全控制器中设置了自己的登录表单。使用LoginForm我已经在安全配置中对此进行了配置。 我想使用自定义登录表单身份验证器来更好地控制身份验证进度,在系统中注册登录, 并做任何我想添加的事情(IP检查等,等等)
所以我的应用程序中还有一个LoginFormAuthenticator类。某种程度上,身份验证过程似乎甚至没有使用以下方法 定制的LoginFormAuthenticator。我的security.yaml是否正确配置?如何使所有配置一起使用?
在某些时候,symfony中的安全性似乎很混乱,我无法理解人们如何正确配置它。
LoginFormAuthenticator:
class LoginFormAuthenticator extends AbstractGuardAuthenticator
{
/**
* Constructor
*
* @param Logger $logger
* @param LoginAttemptManagerInterface $loginAttemptManager
* @param LocationManagerInterface $locationManager
* @param RouterInterface $router
* @param UserPasswordEncoderInterface $userPasswordEncoder
* @param UserRepositoryInterface $userRepository
*/
public function __construct(Logger $logger, LoginAttemptManagerInterface $loginAttemptManager, LocationManagerInterface $locationManager, RouterInterface $router, UserPasswordEncoderInterface $userPasswordEncoder, UserRepositoryInterface $userRepository)
{
$this->_logger = $logger;
$this->_loginAttemptManager = $loginAttemptManager;
$this->_locationManager = $locationManager;
$this->_router = $router;
$this->_userPasswordEncoder = $userPasswordEncoder;
$this->_userRepository = $userRepository;
}
/**
* {@inheritdoc}
*/
protected function getLoginUrl()
{
return $this->_router->generate("login");
}
/**
* {@inheritdoc}
*/
public function getCredentials(Request $request)
{
$credentials = $request->get("login_form");
return [
"username" => $credentials["username"],
"password" => $credentials["password"],
"token" => $credentials["_token"],
];
}
/**
* {@inheritdoc}
*/
public function getUser($credentials, UserProviderInterface $userProvider)
{
$username = $credentials["username"];
try {
$user = $this->_userRepository->findOneByUsername($username);
if (null !== $user && $user instanceof UserInterface) {
/* @var LoginAttempt $loginAttempt */
$loginAttempt = $this->_loginAttemptManager->create();
$user->addLoginAttempt($loginAttempt);
}
}
catch (NoResultException $e) {
return null;
}
catch (NonUniqueResultException $e) {
return null;
}
catch (UsernameNotFoundException $e) {
return null;
}
}
/**
* {@inheritdoc}
*/
public function checkCredentials($credentials, UserInterface $user)
{
/* @var string $rawPassword the unencoded plain password */
$rawPassword = $credentials["password"];
if ($this->_userPasswordEncoder->isPasswordValid($user, $rawPassword)) {
return true;
}
return new CustomUserMessageAuthenticationException("Invalid credentials");
}
/**
* {@inheritdoc}
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
/* @var AbstractUser $user */
$user = $token->getUser();
/* @var LoginAttempt $loginAttempt */
$loginAttempt = $user->getLastLoginAttempt();
$loginAttempt->success();
this->_loginAttemptManager->saveOne($loginAttempt, true);
}
/**
* {@inheritdoc}
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
// without this method the authentication process becomes a loop
}
/**
* {@inheritdoc}
*/
public function start(Request $request, AuthenticationException $authException = null)
{
return new RedirectResponse($this->getLoginUrl());
}
/**
* {@inheritdoc}
*/
public function supports(Request $request)
{
return $request->getPathInfo() != $this->getLoginUrl() || !$request->isMethod(Request::METHOD_POST);
}
/**
* {@inheritdoc}
*/
public function supportsRememberMe()
{
return true;
}
}
SecurityController:
class SecurityController extends AbstractController
{
/**
* @Route(path = "login", name = "login", methods = {"GET", "POST"})
* @Template(template = "security/login.html.twig")
*
* @param AuthenticationUtils $authUtils
* @param Request $request
* @return array
*/
public function login(AuthenticationUtils $authUtils, Request $request)
{
$form = $this->createLoginForm();
if (null !== $authUtils->getLastAuthenticationError()) {
$form->addError(new FormError(
$this->_translator->trans("error.authentication.incorrect-credentials", [], "security")
));
}
if (null != $authUtils->getLastUsername()) {
$form->setData([
"username" => $authUtils->getLastUsername(),
]);
}
// settings are in config/packages/security.yaml
// configuration authenticates user in login form authenticator service
return [
"backgroundImages" => $this->_backgroundImageManager->findAll(),
"form" => $form->createView(),
];
}
/**
* @return FormInterface
*/
private function createLoginForm() : FormInterface
{
$form = $this->createForm(LoginForm::class, null, [
"action" => $this->generateUrl("login"),
"method" => Request::METHOD_POST,
]);
$form->add("submit", SubmitType::class, [
"label" => $this->_translator->trans("btn.login", [], "button"),
"icon_name" => "sign-in",
"translation_domain" => false,
]);
return $form;
}
}
security.yaml:
security:
providers:
user_provider:
entity:
class: App\Entity\Model\AbstractUser
property: username
oauth_provider:
entity:
class: App\Entity\Model\ApiClient
property: name
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
# The API-Oauth-Token-Firewall must be above the API-firewall
api_oauth_token:
pattern: ^/api/oauth/token$
security: false
# The API-firewall must be above the Main-firewall
api:
pattern: ^/api/*
security: true
stateless: true
oauth2: true
provider: oauth_provider
access_denied_handler: App\Service\Api\Security\ApiAccessDeniedHandler
main:
anonymous: true
guard:
authenticators:
- App\Service\Security\LoginFormAuthenticator
access_denied_handler: App\Service\Security\AccessDeniedHandler
provider: user_provider
form_login:
login_path: /login
check_path: /login
default_target_path: / #index
username_parameter: "login_form[username]"
password_parameter: "login_form[password]"
logout:
# the logout path overrides the implementation of the logout method
# in the security controller
path: /logout
target: / #index
remember_me:
secret: '%kernel.secret%'
lifetime: 43200 # 60 sec * 60 min * 12 hours
path: /
remember_me_parameter: "login_form[remember]"
encoders:
App\Entity\Model\AbstractUser:
algorithm: bcrypt
cost: 13
access_control:
# omitted from this question
role_hierarchy:
# omitted from this question
答案 0 :(得分:0)
您是如何提出LoginFormAuthenticator::supports()
的逻辑的?
这样不应该相反:
return 'login' === $request->attributes->get('_route')
&& $request->isMethod('POST');
答案 1 :(得分:0)
显然,我在security.yaml
中配置了两种身份验证形式
因此,我从配置中删除了form_login
键:
security.yaml
security:
providers:
user_provider:
entity:
class: App\Entity\Model\AbstractUser
property: username
oauth_provider:
entity:
class: App\Entity\Model\ApiClient
property: name
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
# The API-Oauth-Token-Firewall must be above the API-firewall
api_oauth_token:
pattern: ^/api/oauth/token$
security: false
# The API-firewall must be above the Main-firewall
api:
pattern: ^/api/*
security: true
stateless: true
oauth2: true
provider: oauth_provider
access_denied_handler: App\Service\Api\Security\ApiAccessDeniedHandler
main:
anonymous: true
guard:
authenticators:
- App\Service\Security\LoginFormAuthenticator
access_denied_handler: App\Service\Security\AccessDeniedHandler
provider: user_provider
logout:
# the logout path overrides the implementation of the logout method
# in the security controller
path: /logout
target: / #index
remember_me:
secret: '%kernel.secret%'
lifetime: 43200 # 60 sec * 60 min * 12 hours
path: /
remember_me_parameter: "login_form[remember]"
并更新了LoginFormAuthenticator
-集成
-还添加了检查CSRF令牌
LoginFormAuthenticator
class LoginFormAuthenticator extends AbstractGuardAuthenticator
{
const FORM = "login_form";
const USERNAME = "username";
const PASSWORD = "password";
const CSRF_TOKEN = "token";
/**
* Constructor
*
* @param CsrfTokenManagerInterface $csrfTokenManager
* @param Logger $logger
* @param LoginAttemptManagerInterface $loginAttemptManager
* @param LocationManagerInterface $locationManager
* @param RouterInterface $router
* @param UserPasswordEncoderInterface $userPasswordEncoder
* @param UserRepositoryInterface $userRepository
*/
public function __construct(CsrfTokenManagerInterface $csrfTokenManager, Logger $logger, LoginAttemptManagerInterface $loginAttemptManager, LocationManagerInterface $locationManager, RouterInterface $router, UserPasswordEncoderInterface $userPasswordEncoder, UserRepositoryInterface $userRepository)
{
$this->_csrfTokenManager = $csrfTokenManager;
$this->_logger = $logger;
$this->_loginAttemptManager = $loginAttemptManager;
$this->_locationManager = $locationManager;
$this->_router = $router;
$this->_userPasswordEncoder = $userPasswordEncoder;
$this->_userRepository = $userRepository;
}
/**
* Get Login URL
*
* @return string
*/
protected function getLoginUrl()
{
return $this->_router->generate("login");
}
/**
* Get Target URL
*
* @return string
*/
protected function getTargetUrl()
{
return $this->_router->generate("index");
}
/**
* {@inheritdoc}
*/
public function getCredentials(Request $request)
{
$credentials = $request->request->get(self::FORM);
$request->getSession()->set(Security::LAST_USERNAME, $credentials["username"]);
return [
self::USERNAME => $credentials["username"],
self::PASSWORD => $credentials["password"],
self::CSRF_TOKEN => $credentials["_token"],
];
}
/**
* {@inheritdoc}
*/
public function getUser($credentials, UserProviderInterface $userProvider)
{
$username = $credentials[self::USERNAME];
try {
$user = $this->_userRepository->findOneByUsername($username);
if (null !== $user && $user instanceof UserInterface) {
/* @var LoginAttempt $loginAttempt */
$loginAttempt = $this->_loginAttemptManager->create();
$user->addLoginAttempt($loginAttempt);
}
return $user;
}
catch (NoResultException $e) {
throw new BadCredentialsException("Authentication failed");
}
catch (NonUniqueResultException $e) {
throw new BadCredentialsException("Authentication failed");
}
}
/**
* {@inheritdoc}
*/
public function checkCredentials($credentials, UserInterface $user)
{
$csrfToken = new CsrfToken(self::FORM, $credentials[self::CSRF_TOKEN]);
if (false === $this->_csrfTokenManager->isTokenValid($csrfToken)) {
throw new InvalidCsrfTokenException('Invalid CSRF token');
}
/* @var string $rawPassword the unencoded plain password */
$rawPassword = $credentials[self::PASSWORD];
if ($this->_userPasswordEncoder->isPasswordValid($user, $rawPassword)) {
return true;
}
/* @var AbstractUser $user */
$loginAttempt = $user->getLastLoginAttempt();
if (null !== $loginAttempt) {
$this->_loginAttemptManager->saveOne($loginAttempt);
}
return new CustomUserMessageAuthenticationException("Invalid credentials");
}
/**
* {@inheritdoc}
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
/* @var AbstractUser $user */
$user = $token->getUser();
/* @var LoginAttempt $loginAttempt */
$loginAttempt = $user->getLastLoginAttempt();
$loginAttempt->setStatus(LoginAttempt::STATUS_AUTHENTICATION_SUCCESS);
if (null !== $loginAttempt) {
$this->_loginAttemptManager->saveOne($loginAttempt);
}
return new RedirectResponse($this->getTargetUrl());
}
/**
* {@inheritdoc}
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception->getMessage());
return new RedirectResponse($this->getLoginUrl());
}
/**
* {@inheritdoc}
*/
public function start(Request $request, AuthenticationException $authException = null)
{
return new RedirectResponse($this->getLoginUrl());
}
/**
* {@inheritdoc}
*/
public function supports(Request $request)
{
return $request->getPathInfo() === $this->getLoginUrl() && $request->isMethod(Request::METHOD_POST);
}
/**
* {@inheritdoc}
*/
public function supportsRememberMe()
{
return true;
}
}