【问题标题】:Symfony2 + FOS using simple_form authenticatorSymfony2 + FOS 使用 simple_form 验证器
【发布时间】:2014-05-06 18:02:18
【问题描述】:

我使用FriendsOfSymfony UserBundle。当我将防火墙中的所有内容设置为form_login 时,它可以工作,但如果我将其设置为simple_form 以使用Custom Authenticator,那么即使帐户被锁定或禁用,它也可以让我登录。我想检查用户是否来自正确的 IP,这就是我创建自定义身份验证器的原因,但似乎来自 FOS 的某些身份验证没有以这种方式处理。如何使用 simple_form 和自定义身份验证器,同时仍保留 FOS UserBundle 的全部功能?

除了标准之外,还有其他方法可以实现其他身份验证吗?也许我做错了什么?我知道我可以更正我的身份验证器的代码以检查锁定/启用等,但我想——因为它实际上已经在 FOS 中完成了——我为什么要这样做?

编辑:另外,我注意到当我使用simple_form 时,不会调用Symfony\Component\Security\Core\User\UserChecker 类的方法。

下面是我的验证码和security.yml

config.yml

services:
    login_authenticator:
        class:     Forex\AlchemyBundle\Security\LoginAuthenticator
        arguments: ["@security.encoder_factory"]

security.yml

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: sha512

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: ROLE_ADMIN

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username_email

    firewalls:
        main:
            pattern: ^/
            simple_form:
                authenticator: login_authenticator
                provider: fos_userbundle
                csrf_provider: form.csrf_provider
            logout:       true
            anonymous:    true

    access_control:
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY } # To be removed
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/, role: ROLE_ADMIN }
        - { path: ^/.*, roles: ROLE_USER }

登录验证器

<?php

namespace Forex\AlchemyBundle\Security;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserProviderInterface;

class LoginAuthenticator implements SimpleFormAuthenticatorInterface
{
    private $encoderFactory;

    public function __construct(EncoderFactoryInterface $encoderFactory)
    {
        $this->encoderFactory = $encoderFactory;
    }

    public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
    {
        try {
            $user = $userProvider->loadUserByUsername($token->getUsername());
        } catch (UsernameNotFoundException $e) {
            throw new AuthenticationException('Invalid username or password');
        }

        $encoder = $this->encoderFactory->getEncoder($user);
        $passwordValid = $encoder->isPasswordValid(
            $user->getPassword(),
            $token->getCredentials(),
            $user->getSalt()
        );

        if ($passwordValid) {

            $request = Request::createFromGlobals();

            $current_ip = $request->server->get('REMOTE_ADDR');

            $user->setLoggedIP($current_ip);

            if (!$user->isValidIP()) {
                throw new AuthenticationException(
                    "You cannot login from your location.",
                    100
                );
            }

            return new UsernamePasswordToken(
                $user,
                $user->getPassword(),
                $providerKey,
                $user->getRoles()
            );
        } else {
            // TODO: Check if there weren't too many tries to login
        }

        throw new AuthenticationException('Invalid username or password');
    }

    public function supportsToken(TokenInterface $token, $providerKey)
    {
        return $token instanceof UsernamePasswordToken
            && $token->getProviderKey() === $providerKey;
    }

    public function createToken(Request $request, $username, $password, $providerKey)
    {
        return new UsernamePasswordToken($username, $password, $providerKey);
    }

}

【问题讨论】:

  • 我认为您需要为您的自定义身份验证器创建一个自定义提供程序,并使用您的自定义提供程序加上http_basic: ~(用于基本身份验证)向您的防火墙添加另一个部分;并将当前部分保留在 FOS 捆绑包的防火墙中
  • 我想避免创建自定义提供程序 - SimpleForm 背后的想法不是不必创建它吗?
  • 所以您的意思是合并自定义身份验证器和 FOS 而不创建自定义提供程序,对吗?
  • 是的。似乎它有效,只是标准UserAuthenticationProvider 调用类UserCheckerSimpleAuthenticationProvider 没有。我想我将只导入UserChecker 并在 LoginAuthenticator 中自己调用它的方法。我只是担心某些其他功能可能无法正常工作,而我可能没有意识到这一点。
  • 这也可能有效。我不确定这是否有帮助,但不仅要实现 SimpleFormAuthenticatorInterface 还要扩展 FOSUserBundle 身份验证器怎么样?在这种情况下,您需要确保不要覆盖父函数。

标签: php symfony authentication fosuserbundle


【解决方案1】:

我遇到了同样的问题。这是我解决它的方法。

您必须在 Authenticator 的 authenticateToken() 方法中调用 UserChecker 类的 checkPreAuth() 和 checkPostAuth() 方法。

这样做是这样的:

1) 配置 user_checker 服务:

services:
    app.user_checker:
        class: AppBundle\Security\UserChecker

2)将autenticator配置为服务并注入user_checker服务:

services:
    app.my_authenticator:
        class:     AppBundle\Security\MyAuthenticator
        arguments: ["@app.user_checker", "@security.password_encoder"]

3) 现在你可以在 authenticateToken() 上调用 checkPreAuth() 和 checkPostAuth()

无论如何,我认为 symfony 方法是正确的,因为在我的情况下,y 需要在 simple_form 中执行与 login_form 不同的检查。

【讨论】:

    猜你喜欢
    • 2014-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-06
    • 2015-12-09
    相关资源
    最近更新 更多