php 自动注册后用户认证

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5886713/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 22:47:55  来源:igfitidea点击:

Automatic post-registration user authentication

phpsymfonyregistration

提问by Problematic

We're building a business app from the ground up in Symfony 2, and I've run into a bit of a snag with the user registration flow: after the user creates an account, they should be automatically logged in with those credentials, instead of being immediately forced to provide their credentials again.

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

Anyone had any experience with this, or able to point me in the right direction?

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

回答by Chase

Symfony 4.0

Symfony 4.0

This process hasnt changed from symfony 3 to 4 but here is an example using the newly recommended AbstractController. Both the security.token_storageand the sessionservices are registered in the parent getSubscribedServicesmethod so you dont have to add those in your controller.

这个过程并没有从 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.x - Symfony 3.0.x

As of symfony 2.6 security.contextis deprecated in favor of security.token_storage. The controller can now simply be:

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

}

While this is deprecated you can still use security.contextas it has been made to be backward compatible. Just be ready to update it for Symfony 3

虽然这已被弃用,但您仍然可以使用security.context它,因为它已向后兼容。准备好为 Symfony 3 更新它

You can read more about the 2.6 changes for security here: https://github.com/symfony/symfony/blob/2.6/UPGRADE-2.6.md

您可以在此处阅读有关 2.6 安全更改的更多信息:https: //github.com/symfony/symfony/blob/2.6/UPGRADE-2.6.md

Symfony 2.3.x

Symfony 2.3.x

To Accomplish this in symfony 2.3 you can no longer just set the token in the security context. You also need to save the token to the session.

要在 symfony 2.3 中实现这一点,您不能再仅仅在安全上下文中设置令牌。您还需要将令牌保存到会话中。

Assuming a security file with a firewall like:

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

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

And a controller action similar too:

还有一个类似的控制器动作:

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
    }

}

For the token creation you will want to create a UsernamePasswordToken, This accepts 4 parameters: User Entity, User Credentials, Firewall Name, User Roles. You dont need to provide the user credentials for the token to be valid.

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

Im not 100% sure that setting the token on the security.contextis necessary if you are just going to redirect right away. But it doesnt seem to hurt so i have left it.

security.context如果您要立即重定向,我不能 100% 确定需要在 上设置令牌。但它似乎并不痛,所以我离开了它。

Then the important part, setting the session variable. The variables naming convention is _security_followed by your firewall name, in this case mainmaking _security_main

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

回答by Problematic

Figured this one out, finally.

想通了这一点,终于。

After user registration, you should have access to an object instanceof whatever you've set as your user entity in your provider configuration. The solution is to create a new token with that user entity and pass it into the security context. Here's an example based on my setup:

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

RegistrationController.php:

RegistrationController.php:

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

Where mainis the name of the firewall for your application (thanks, @Joe). That's really all there is to it; the system now considers your user fully logged in as the user they've just created.

main您的应用程序的防火墙名称在哪里(谢谢,@Joe)。这就是它的全部内容;系统现在将您的用户视为他们刚刚创建的用户完全登录。

EDIT: Per @Miquel's comment, I've updated the controller code sample to include a sensible default role for a new user (though obviously this can be adjusted according to your application's specific needs).

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

回答by Cédric Nirousset

If you have a UserInterface object (and that should be the case most of the time) you might want to use the getRoles function that it implements for the last argument. So if you create a function logUser, it should looks like that:

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

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

回答by Sam Hanes

I'm using Symfony 2.2 and my experience was slightly different than Problematic's, so this is a combined version of all the info from this question plus some of my own.

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

I think Joeis wrong about the value of $providerKey, the third parameter to the UsernamePasswordTokenconstructor. It's supposed to be the key of an authentication (not user) provider. It's used by the authentication system to distinguish between tokens created for different providers. Any provider which descends from UserAuthenticationProviderwill only authenticate tokens whose provider key matches its own. For example, the UsernamePasswordFormAuthenticationListenersets the key of the token it creates to match that of its corresponding DaoAuthenticationProvider. That lets a single firewall have multiple username+password providers without them stepping on each other. We therefore need to choose a key that won't conflict with any other providers. I use 'new_user'.

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

I have a few systems in other parts of my application that depend on the authentication success event, and that isn't fired by just setting the token on the context. I had to get the EventDispatcherfrom the container and fire the event manually. I decided against also firing an interactive login eventbecause we're authenticating the user implicitly, not in response to an explicit login request.

我的应用程序的其他部分有一些系统依赖于身份验证成功事件,并且仅通过在上下文中设置令牌不会触发。我必须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 ) );

Note that use of $this->get( .. )assumes the snippet is in a controller method. If you're using the code somewhere else you'll have to change those to call ContainerInterface::get( ... )in a way appropriate to the environment. As it happens my user entities implement UserInterfaceso I can use them directly with the token. If yours don't you'll have to find a way to convert them to UserInterfaceinstances.

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

That code works, but I feel like it's hacking around Symfony's authentication architecture rather than working with it. It would probably be more correct to implement a new authentication providerwith its own token class rather than hiHymaning the UsernamePasswordToken. Also, using a proper provider would mean that the events were handled for you.

该代码有效,但我觉得它是在 Symfony 的身份验证架构周围进行黑客攻击,而不是使用它。它可能是更正确实施新的认证供应商有自己的代币类,而不是霸王硬上弓UsernamePasswordToken。此外,使用适当的提供程序意味着事件已为您处理。

回答by daemonl

In case anyone has the same follow-on question which kept me coming back to here:

万一有人有同样的后续问题,这让我回到这里:

Calling

打电话

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

only effects the current security.contextfor the route used.

只影响所security.context用路线的电流。

I.e. you can only log in a user from a url within the firewall's control.

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

(Add an exception for the route if needed - IS_AUTHENTICATED_ANONYMOUSLY)

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

回答by Nim

As Problematic here already mentioned, this elusive $providerKey parameter is in reality nothing more than the name of your firewall rule, 'foobar' in the case of the example below.

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

firewalls:
    foobar:
        pattern:    /foo/

回答by Diego Castro

I tried all the answers here and none worked. The only way I could authenticate my users on a controller is by making a subrequest and then redirecting. Here is my code, I'm using silex but you can easily adapt it to symfony2:

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

回答by Etienne No?l

With Symfony 4.4, you can simply do the following in your controller method (see from the Symfony documentation: https://symfony.com/doc/current/security/guard_authentication.html#manually-authenticating-a-user):

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

One important thing, make sure your firewall is not set to lazy. If it is, the token will never be stored in the session and you will never get logged in.

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

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

回答by Nico

On Symfony version 2.8.11 (probably working for older and newer versions), if you use FOSUserBundlesimply do this :

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

No need to dispatch event as I've seen in other solutions.

无需像我在其他解决方案中看到的那样调度事件。

inpired from FOS\UserBundle\Controller\RegistrationController::authenticateUser

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

(from composer.json FOSUserBundle version : "friendsofsymfony/user-bundle": "~1.3")

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