Php 自动注册后用户身份验证

Php 自动注册后用户身份验证,php,symfony,registration,Php,Symfony,Registration,我们正在Symfony 2中从头开始构建一个商业应用程序,我在用户注册流程中遇到了一点障碍:用户创建帐户后,他们应该使用这些凭据自动登录,而不是立即被迫再次提供凭据 有没有人有过这样的经验,或者能够为我指出正确的方向?最终找到了这个答案 用户注册后,您应该可以访问在提供程序配置中设置为用户实体的对象实例。解决方案是使用该用户实体创建一个新令牌,并将其传递到安全上下文中。以下是基于我的设置的示例: RegistrationController.php: $token = new UsernameP

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


有没有人有过这样的经验,或者能够为我指出正确的方向?

最终找到了这个答案

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

RegistrationController.php:

$token = new UsernamePasswordToken($userEntity, null, 'main', array('ROLE_USER'));
$this->get('security.context')->setToken($token);
其中
main
是应用程序的防火墙名称(谢谢,@Joe)。这就是它的全部;系统现在将您的用户视为刚刚创建的用户完全登录


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

正如前面提到的问题一样,这个难以捉摸的$providerKey参数实际上只不过是防火墙规则的名称,在下面的示例中是“foobar”

firewalls:
    foobar:
        pattern:    /foo/

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

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

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

召唤

$this->container->get('security.context')->setToken($token); 
仅影响所用路由的当前
安全上下文

即,您只能从防火墙控制范围内的url登录用户


(如果需要,为路由添加一个例外-
经过匿名身份验证)

我使用的是Symfony 2.2,我的经验与之略有不同,因此这是来自此问题的所有信息加上我自己的一些信息的组合版本

我认为
$providerKey
的值是错误的,它是
UsernamePasswordToken
构造函数的第三个参数。它应该是身份验证(而不是用户)提供者的密钥。身份验证系统使用它来区分为不同提供者创建的令牌。从派生的任何提供程序将仅对其提供程序密钥与其自身密钥匹配的令牌进行身份验证。例如,设置它创建的令牌的密钥以匹配其相应令牌的密钥。这样,单个防火墙就可以拥有多个用户名+密码提供程序,而无需彼此干涉。因此,我们需要选择一个不会与任何其他提供者冲突的密钥。我使用
“新用户”

在我的应用程序的其他部分中,我有一些依赖于的系统,这些系统不是通过在上下文中设置令牌来触发的。我必须从容器中获取
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 ) );
请注意,使用
$this->get(..)
假设代码段位于控制器方法中。如果您在其他地方使用代码,则必须更改这些代码,以适合环境的方式调用
ContainerInterface::get(…)
。碰巧我的用户实体实现了
UserInterface
,因此我可以直接将它们与令牌一起使用。如果你没有,你必须找到一种方法将它们转换成
UserInterface
实例


这段代码可以工作,但我觉得它是在攻击Symfony的身份验证架构,而不是使用它。使用自己的令牌类可能比劫持
UsernamePasswordToken
更为正确。另外,使用合适的提供者意味着事件已为您处理。

我在这里尝试了所有答案,但没有一个有效。在控制器上验证用户身份的唯一方法是发出子请求,然后重定向。这是我的代码,我使用的是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'));

Symfony 4.0

此过程没有从Symfony 3更改为4,但下面是一个使用新推荐的
AbstractController
的示例。
security.token\u存储
session
服务都注册在父
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开始,安全性。上下文被弃用,取而代之的是安全性。令牌存储。控制器现在可以是:

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安全性更改的更多信息:

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
上设置令牌。但它似乎不痛,所以我把它留下了

然后是重要的部分,设置会话变量。变量命名约定是
\u security\u
fo
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.).
}
// 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
        );
    }
}
firewalls:
    main:
        anonymous: ~ # this and not 'lazy'