自动注册后用户身份验证

时间:2011-05-04 16:31:49

标签: php symfony registration

我们正在Symfony 2中从头开始构建业务应用程序,并且我在用户注册流程中遇到了一些问题:在用户创建帐户后,他们应该自动登录凭证,而不是立即被迫再次提供其凭据。

任何人都有过这方面的经验,或者能指出我正确的方向吗?

9 个答案:

答案 0 :(得分:139)

Symfony 4.0

此过程没有从symfony 3更改为4,但这是使用新推荐的AbstractController的示例。 security.token_storagesession服务均在父getSubscribedServices方法中注册,因此您无需在控制器中添加这些服务。

use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use YourNameSpace\UserBundle\Entity\User;

class LoginController extends AbstractController{

    public function registerAction()
    {    
        $user = //Handle getting or creating the user entity likely with a posted form
        $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
        $this->container->get('security.token_storage')->setToken($token);
        $this->container->get('session')->set('_security_main', serialize($token));
        // The user is now logged in, you can redirect or do whatever.
    }

}

Symfony 2.6.x - Symfony 3.0.x

从symfony 2.6 security.context开始,不推荐使用security.token_storage。控制器现在可以简单地:

use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use YourNameSpace\UserBundle\Entity\User;

class LoginController extends Controller{

    public function registerAction()
    {    
        $user = //Handle getting or creating the user entity likely with a posted form
        $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
        $this->get('security.token_storage')->setToken($token);
        $this->get('session')->set('_security_main', serialize($token));
    }

}

虽然不推荐使用此功能,但您仍然可以使用security.context,因为它已向后兼容。只是准备好为Symfony 3更新它

您可以在此处详细了解2.6安全性更改:https://github.com/symfony/symfony/blob/2.6/UPGRADE-2.6.md

Symfony 2.3.x

要在symfony 2.3中完成此操作,您不能再在安全上下文中设置令牌。您还需要将令牌保存到会话中。

假设一个带有防火墙的安全文件:

// app/config/security.yml
security:
    firewalls:
        main:
            //firewall settings here

控制器动作也类似:

use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use YourNameSpace\UserBundle\Entity\User;

class LoginController extends Controller{

    public function registerAction()
    {    
        $user = //Handle getting or creating the user entity likely with a posted form
        $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
        $this->get('security.context')->setToken($token);
        $this->get('session')->set('_security_main',serialize($token));
        //Now you can redirect where ever you need and the user will be logged in
    }

}

对于令牌创建,您需要创建UsernamePasswordToken,这将接受4个参数:用户实体,用户凭据,防火墙名称,用户角色。您无需提供令牌的用户凭据即可生效。

我不是100%确定如果您要立即重定向,则必须在security.context上设置令牌。但它似乎没有伤害所以我离开了它。

然后是重要的部分,设置会话变量。变量命名约定为_security_,后跟防火墙名称,在这种情况下main制作_security_main

答案 1 :(得分:65)

最后想出这个。

用户注册后,您应该可以访问在提供程序配置中设置为用户实体的任何对象实例。解决方案是使用该用户实体创建新令牌并将其传递到安全上下文中。以下是基于我的设置的示例:

RegistrationController.php:

$token = new UsernamePasswordToken($userEntity, null, 'main', array('ROLE_USER'));
$this->get('security.context')->setToken($token);

其中main是应用程序防火墙的名称(感谢@Joe)。这真的就是它的全部;系统现在认为您的用户已完全以他们刚刚创建的用户身份登录。

编辑:Per @ Miquel的评论,我已经更新了控制器代码示例,以便为新用户提供合理的默认角色(尽管显然可以根据应用程序的特定需求进行调整)。

答案 2 :(得分:6)

如果你有一个UserInterface对象(大多数情况下应该是这种情况)你可能想要使用它为最后一个参数实现的getRoles函数。 因此,如果您创建一个函数logUser,它应该是这样的:

public function logUser(UserInterface $user) {
    $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
    $this->container->get('security.context')->setToken($token);
}

答案 3 :(得分:6)

我正在使用Symfony 2.2,我的经验与Problematic's略有不同,所以这是此问题的所有信息加上我自己的一些信息的组合版本。

我认为Joe$providerKey的值是错误的,UsernamePasswordToken'new_user'构造函数的第三个参数。它应该是身份验证(而非用户)提供商的关键。身份验证系统使用它来区分为不同提供程序创建的令牌。任何从UserAuthenticationProvider下降的提供者只会验证其提供者密钥与其自身匹配的令牌。例如,UsernamePasswordFormAuthenticationListener设置其创建的令牌的键以匹配其对应的DaoAuthenticationProvider的键。这使得单个防火墙具有多个用户名+密码提供程序,而不会相互踩踏。因此,我们需要选择一个不会与任何其他提供商冲突的密钥。我使用EventDispatcher

我的应用程序的其他部分中有一些系统依赖于authentication success event,并且只是在上下文中设置令牌而不会触发。我必须从容器中获取use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\AuthenticationEvents; use Symfony\Component\Security\Core\Event\AuthenticationEvent; $user = // get a Symfony user instance somehow $token = new UsernamePasswordToken( $user, null, 'new_user', $user->getRoles() ); $this->get( 'security.context' )->setToken( $token ); $this->get( 'event_dispatcher' )->dispatch( AuthenticationEvents::AUTHENTICATION_SUCCESS, new AuthenticationEvent( $token ) ); 并手动触发事件。我决定不再触发interactive login event,因为我们隐式地对用户进行身份验证,而不是响应显式登录请求。

$this->get( .. )

请注意,使用ContainerInterface::get( ... )假设代码段位于控制器方法中。如果您在其他地方使用代码,则必须更改那些以适合环境的方式调用UserInterface的代码。碰巧我的用户实体实现了UserInterface,所以我可以直接使用它们。如果你不这样做,你将不得不找到一种方法将它们转换为UsernamePasswordToken个实例。

该代码有效,但我觉得它是围绕Symfony的身份验证架构而不是使用它。 implement a new authentication provider使用自己的令牌类而不是劫持{{1}}可能更为正确。此外,使用适当的提供商意味着事件是为您处理的。

答案 4 :(得分:4)

如果有人有同样的后续问题让我回到这里:

致电

$this->container->get('security.context')->setToken($token); 

仅影响所用路线的当前security.context

即。您只能从防火墙控件内的URL登录用户。

(如果需要,添加路线例外 - IS_AUTHENTICATED_ANONYMOUSLY

答案 5 :(得分:2)

这里提到的有问题,这个难以捉摸的$ providerKey参数实际上只不过是防火墙规则的名称,在下面的例子中是'foobar'。

firewalls:
    foobar:
        pattern:    /foo/

答案 6 :(得分:2)

我在这里尝试了所有的答案,但都没有效果。我可以在控制器上验证用户身份的唯一方法是进行子请求,然后重定向。这是我的代码,我使用的是silex,但您可以轻松地将其调整为symfony2:

$subRequest = Request::create($app['url_generator']->generate('login_check'), 'POST', array('_username' => $email, '_password' => $password, $request->cookies->all(), array(), $request->server->all());

$response = $app->handle($subRequest, HttpKernelInterface::MASTER_REQUEST, false);

return $app->redirect($app['url_generator']->generate('curriculos.editar'));

答案 7 :(得分:2)

使用Symfony 4.4,您可以简单地在控制器方法中执行以下操作(请参见Symfony文档:https://symfony.com/doc/current/security/guard_authentication.html#manually-authenticating-a-user):

// src/Controller/RegistrationController.php
// ...

use App\Security\LoginFormAuthenticator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;

class RegistrationController extends AbstractController
{
    public function register(LoginFormAuthenticator $authenticator, GuardAuthenticatorHandler $guardHandler, Request $request)
    {
        // ...

        // after validating the user and saving them to the database
        // authenticate the user and use onAuthenticationSuccess on the authenticator
        return $guardHandler->authenticateUserAndHandleSuccess(
            $user,          // the User object you just created
            $request,
            $authenticator, // authenticator whose onAuthenticationSuccess you want to use
            'main'          // the name of your firewall in security.yaml
        );
    }
}

一件重要的事情,请确保您的防火墙未设置为lazy。如果是这样,令牌将永远不会存储在会话中,并且您也永远不会登录。

firewalls:
    main:
        anonymous: ~ # this and not 'lazy'

答案 8 :(得分:1)

在Symfony版本2.8.11(可能适用于较旧版本和较新版本),如果您使用FOSUserBundle ,只需执行此操作:

try {
    $this->container->get('fos_user.security.login_manager')->loginUser(
    $this->container->getParameter('fos_user.firewall_name'), $user, null);
} catch (AccountStatusException $ex) {
    // We simply do not authenticate users which do not pass the user
    // checker (not enabled, expired, etc.).
}

无需按照我在其他解决方案中看到的那样发送事件。

来自FOS \ UserBundle \ Controller \ RegistrationController :: authenticateUser

(来自composer.json FOSUserBundle版本:“friendsofsymfony / user-bundle”:“~1.3”)