自动注册后用户身份验证

Pro*_*tic 108 php registration symfony

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

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

Cha*_*ase 140

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.
    }

}
Run Code Online (Sandbox Code Playgroud)

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));
    }

}
Run Code Online (Sandbox Code Playgroud)

虽然这已被弃用,但您仍然可以使用,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
Run Code Online (Sandbox Code Playgroud)

和控制器动作类似:

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
    }

}
Run Code Online (Sandbox Code Playgroud)

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

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

然后是重要的部分,设置会话变量.变量命名约定_security_后跟防火墙名称,在本例中为mainmake_security_main

  • @Chausser设法让它发挥作用.你的答案是完全正确的(并且它已经更新),它只有当你在**相同的目标防火墙**或**下调用`setToken(..)`而没有经过身份验证**时才能正常工作. (3认同)
  • 疯狂的事情发生在没有`$ this-> get('session') - > set('_ security_main',serialize($ token));`的情况下.谢谢@Chausser! (2认同)

Pro*_*tic 65

最后想出这个.

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

RegistrationController.php:

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

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

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

  • 对于Symfony 2的发布版本,这是不对的.您需要将用户的角色作为第四个参数传递给UsernamePasswordToken构造函数,否则它将被标记为未经身份验证,并且用户将不具有任何角色. (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);
}
Run Code Online (Sandbox Code Playgroud)


Sam*_*nes 6

我正在使用Symfony 2.2,我的经验与有问题的有点不同,所以这是这个问题的所有信息和我自己的一些信息的组合版本.

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

我的应用程序的其他部分中有一些系统依赖于身份验证成功事件,并且只是在上下文中设置令牌而不会触发.我必须EventDispatcher从容器中取出并手动触发事件.我决定不再触发交互式登录事件,因为我们隐式地对用户进行身份验证,而不是响应显式登录请求.

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 ) );
Run Code Online (Sandbox Code Playgroud)

请注意,使用$this->get( .. )假定代码段是在控制器方法中.如果您在其他地方使用代码,则必须以ContainerInterface::get( ... )适合环境的方式更改要调用的代码.当我的用户实体实现时UserInterface,我可以直接使用令牌.如果你不这样做,你将不得不找到一种方法将它们转换为UserInterface实例.

该代码有效,但我觉得它是围绕Symfony的身份验证架构而不是使用它.使用自己的令牌类实现新的身份验证提供程序而不是劫持它可能更为正确UsernamePasswordToken.此外,使用适当的提供商意味着事件是为您处理的.


Eti*_*oël 5

使用 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
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

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

firewalls:
    main:
        anonymous: ~ # this and not 'lazy'
Run Code Online (Sandbox Code Playgroud)