In part 1, we discussed the basics of setting up a security system in our app (database and security.yml settings). We also covered the pre-registration stage where a user verifies their invitation status with the app.

第1部分中 ,我们讨论了在应用程序中设置安全系统的基本知识(数据库和security.yml设置)。 我们还介绍了预注册阶段,在该阶段用户可以通过应用验证其邀请状态。

Symfony2 logo

In this article, we will talk about registration, logins and post-login actions.


表单,数据库等 (Form, database, and more)

Registration is done through a form. The user will enter information like email, user name, password, confirmed password, and accept a disclaimer in some cases.

注册是通过表格完成的。 用户将输入诸如电子邮件,用户名,密码,确认的密码之类的信息,并在某些情况下接受免责声明。

We also know that a user object will ultimately be persisted in the user table.


During this persistence process, we must be aware that:


  1. Some form input will be used in populating a user object (like username, password);

    某些表单输入将用于填充用户对象(例如usernamepassword );

  2. Some user properties will be set by the app (like created, a date/time field to store when the user registers);

    应用程序将设置一些用户属性(例如created ,用户注册时要存储的日期/时间字段);

  3. Some form inputs are merely for verification and discarded (like retyped password, a check on the disclaimer).


We must have a way to create a “link” between a form and the underlying table, and specify the above requirements.


In Symfony, we achieve this by declaring a special form type class associated with an entity. In this case, a RegistrationType manages which fields to display, which fields are mapped (to a field), etc.

在Symfony中,我们通过声明与实体关联的特殊表单类型类来实现此目的。 在这种情况下, RegistrationType管理要显示的字段,映射的字段(映射到字段)等。

This class (RegistrationType) is defined in src/AppBundle/Form/Type/RegistrationType.php:

此类( RegistrationType )在src/AppBundle/Form/Type/RegistrationType.php

class RegistrationType extends AbstractType

    public function buildForm(FormBuilderInterface $builder, array $options)
        $builder->add('username', 'text', ['label'=>'User Name'])
                ->add('password', 'password',['label'=>'Password'])
                ->add('confirm', 'password', ['mapped' => false,'label'=>'Re-type password'])
                ->add('homepage', 'text',['label'=>'Homepage'])
                ->add('email', 'hidden', ['label'=>'email'])
                ->add('save', 'submit', ['label'=>'Register'])

    public function getName()
        return 'registration';

    public function setDefaultOptions(OptionsResolverInterface $resolver)
            'data_class' => 'AppBundle\Entity\User',


We have several ->add() calls that add a form field to be displayed and mapped to the underlying table field; or a form field to be displayed but not mapped to the underlying table field. Any table field not added will not be displayed and thus not be populated by the user.

我们有几个->add()调用,它们添加了一个要显示并映射到基础表字段的表单字段; 或要显示但未映射到基础表字段的表单字段。 任何未添加的表字段都不会显示,因此用户不会填充。

Let’s take a look at some examples:


add('username', 'text', ['label'=>'User Name'])

This adds a form field of text type, mapping to table field username, that has a label ‘User Name’.

这将添加一个文本类型的表单字段,该字段映射到表字段username ,并带有标签“ User Name”。

add('confirm', 'password', ['mapped' => false,'label'=>'Re-type password'])

This adds a form field of password type, but is not mapped to a table field, and has a label ‘Retype password’.


add('email', 'hidden', ['label'=>'email'])

This adds a hidden form field, mapping to table field email. The label setting here is useless but there is no harm in having it there.

这将添加一个隐藏的表单字段,映射到表字段email 。 此处的标签设置无用,但在此处放置标签没有任何危害。

Once the RegistrationType is defined, we can move on to the real registration (after pre-registration passes):

定义RegistrationType ,我们可以继续进行实际注册(在预注册通过之后):

$registration = new User();

$form         = $this->createForm(new RegistrationType(), $registration, ['action' => $this->generateUrl('create'), 'method' => 'POST']);

return $this->render('AppBundle:Default:register2.html.twig', ['form' => $form->createView(), 'email' => $email]);

We created a new user instance, and then used createForm to create a form (with added action and method attributes) with the constraints and manifestations declared in RegistrationType which is associated with the user object ($registration).

我们创建了一个新的用户实例,然后使用createForm创建一个表单(具有添加的操作和方法属性),该表单具有在RegistrationType中声明的与用户对象( $registration )关联的约束和表现形式。

Finally, we display the registration form.


在视图模板中呈现表单 (Rendering a form in a view template)

The rendered registration form looks like this:


Register screen

The code to render the form is like this:


<form class="form-signin" name='register_form' id='register_form' method='post' action='{{path('create')}}'>
	{{ form_widget(form.username, {'attr': {'class': 'form-control', 'placeholder':'User Name'}}) }}<br>

    {{ form_widget(form.password, {'attr': {'class': 'form-control', 'placeholder':'Password'}}) }}

    {{ form_widget(form.confirm, {'attr': {'class': 'form-control', 'placeholder':'Confirm Password'}}) }}

    {{ form_widget(form.homepage, {'attr': {'class': 'form-control', 'placeholder':'个人主页'}}) }}
    {{ form_widget(form.email, {'attr': {'value': email}}) }}
    <div class="checkbox">
            <input type="checkbox" value="remember-me" required checked>Disclaimer
    <button class="btn btn-lg btn-primary btn-block" type="submit">Register</button>

I have to confess that to display a form created by the above process is not an easy job. Luckily, Twig provides a few helper functions for us to customize this rendered form.

我不得不承认,要显示通过上述过程创建的表单并非易事。 幸运的是,Twig为我们提供了一些帮助程序功能来自定义此呈现的表单。

{{ form_widget(form.password, {'attr': {'class': 'form-control', 'placeholder':'Password'}}) }}

Using the form_widget helper, the first parameter passed in is the form field (password). The more important part is the second parameter, which further defines the rendered HTML5 element. In the above code, we specified that the <input> element for password should have a CSS class form-control (which is a Bootstrap form class) and has a placeholder.

使用form_widget帮助器,传入的第一个参数是表单字段( password )。 更为重要的部分是第二个参数,它进一步定义了呈现HTML5元素。 在上面的代码中,我们指定了password<input>元素应具有CSS类form-control (这是Bootstrap表单类)并具有占位符。

Note that we did not specify which type this form field should be – it should be a password field as we are to input a password. The form_widget is smart enough (or more precisely, $form = $this->createForm(...) is smart enough) to create form elements based on their respective definition in the RegistrationType declaration.

请注意,我们没有指定此表单字段应为哪种类型-输入密码时应为密码字段。 form_widget足够聪明(或者更精确地说, $form = $this->createForm(...)足够聪明),可以根据RegistrationType声明中的各自定义创建表单元素。

创建用户 (Creating the user)

When the user clicks the “Register” button, the information will be processed further and if all goes well, a user will be created.


public function createAction(Request $req)
        $em   = $this->getDoctrine()->getManager();
        $form = $this->createForm(new RegistrationType(), new User());

        $user= new User();
        $user= $form->getData();

        $user->setCreated(new \DateTime());

        $pwd=$encoder->encodePassword($user, $pwd);

        $url = $this->generateUrl('login');
        return $this->redirect($url);


In this segment of code, we will do a lot of things related to actually creating a user from a form input.


  1. $this->createForm will be called again to generate a form based on RegistrationType.


  2. The form object will process the user input.

  3. We create an empty User object and by using $form->getData(), we magically assign the form input to object properties.

    我们创建一个空的User对象,并使用$form->getData() ,将表单输入神奇地分配给对象属性。

  4. We start to assign those properties not populated by the user: creation date, role, gravatar, etc.

  5. The user can only input their password in plain text and the app takes the responsibility of hashing it. That is exactly what these two lines of code are doing.

    用户只能以纯文本形式输入密码,应用程序负责对密码进行哈希处理。 这就是这两行代码所做的。
$encoder = $this->container->get('security.password_encoder');
$pwd = $encoder->encodePassword($user, $pwd);

Note that in these two lines, we don’t even tell the code which encoding method we are actually using. Symfony just looks for the encoder from the app configuration and hashes the plain text.

请注意,在这两行中,我们甚至没有告诉代码实际使用的是哪种编码方法。 Symfony只是从应用程序配置中寻找encoder ,并对纯文本进行哈希处理。

NOTE: Your outdated PHP installation might not include bcrypt. If that is the case, please use composer to install the ircmaxell/password-compat library.

注意:过时PHP安装可能不包含bcrypt 。 在这种情况下,请使用composer安装ircmaxell/password-compat库。

NOTE: Symfony 2 form input processing and database manipulation is safe in terms that it handles all the necessary escaping to prevent from malicious inputs and SQL injections. Thus we can assign the inputs to respective fields.

注意: Symfony 2表单输入处理和数据库操作很安全,因为它可以处理所有必要的转义操作,以防止恶意输入和SQL注入。 因此,我们可以将输入分配给各个字段。

登录和发布登录 (Login and post login)

When we do our user management as stipulated above, the login process is simple. We have already defined two routes related to login:

当我们按照上述要求进行用户管理时,登录过程很简单。 我们已经定义了两个与登录有关的路由:

    path: /login
    defaults: { _controller: AppBundle:Security:login}    
    path: /login_check

Next, we will create a template to display a basic login form:


<form class="form-signin" method='post' action='{{path('login_check')}}'>
	{% if error %}
    <div class='red'>{{ error.message }}</div><br>
    {% endif %}
    <label for="inputName" class="sr-only">User Name</label>
    <input type="text" id="inputName" name='_username' class="form-control" placeholder="User Name" required autofocus>
    <label for="inputPassword" class="sr-only">Password</label>
    <input type="password" id="inputPassword" name="_password" class="form-control" placeholder="Password" required>
    <div class="checkbox">
        	<input type="checkbox" value="remember-me" required checked>Disclaimer
    <button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>

There are only 2 things to notice:


  1. The action of this login form must point to {{path('login_check'}}, or /login_check. We don’t need to implement this controller. Symfony’s security system will do this for us and the default is good enough.

    该登录表单的操作必须指向{{path('login_check'}}/login_check ,我们不需要实现此控制器,Symfony的安全系统将为我们执行此操作,并且默认值足够好。

  2. In our example, we are using username + password as the credentials. Thus, the two inputs in the form MUST be named “_username” and “_password“. This is required by Symfony’s security system.

    在我们的示例中,我们使用用户名+密码作为凭据。 因此,两个输入格式必须分别命名为“ _username ”和“ _password ”。 这是Symfony的安全系统要求的。

We may also notice that as we are not using a “Form type” to link the login information to the underlying user object (like we do for registration), and instead we leave this to the security interface, we constructed the form widgets all by ourselves.


That’s it. The user can now input the username and password and log in.

而已。 用户现在可以输入用户名和密码并登录。

Login screen

After a successful login, we need to be aware of a few things:


  1. In a controller, we can use $this->getUser() to get the current user’s information (a user record in the form of a User object).


  2. In Twig, we can use certain helper functions to access the user object.


As we can see in Part 1, is_granted('ROLE_ADMIN') is used to determine if the current user is in the ADMIN group.

正如我们在第1部分中看到的那样, is_granted('ROLE_ADMIN')用于确定当前用户是否在ADMIN组中。

It is quite interesting to notice that Symfony’s security control interface does not provide an intuitive way to allow the app to do some post-login actions. There is NO such thing as:

值得注意的是,Symfony的安全控制界面没有提供允许应用程序执行某些登录后操作的直观方法。 没有这样的事情:

	path: /login_after

In our app, we really need to do something after a user logs in. We need to update the user’s last login date and time (logged field). To make this simple task happen, we need to tweak our app a bit.

在我们的应用程序中,我们确实需要在用户登录后做一些事情。我们需要更新用户的上次登录日期和时间(已logged字段)。 为了使这个简单的任务实现,我们需要稍微调整一下我们的应用程序。

First, we register a service (for “after successful login event”) in service.yml:


        class: AppBundle\Handler\AuthenticationSuccessHandler
        arguments: [@security.http_utils, @service_container, {}]
            - { name: 'monolog.logger', channel: 'security'}

Next we create a src/AppBundle/Handler/AuthenticationHandler.php file:


class AuthenticationSuccessHandler extends DefaultAuthenticationSuccessHandler
    protected $container;
    public function __construct(HttpUtils $httpUtils, \Symfony\Component\DependencyInjection\ContainerInterface $cont, array $options)
        parent::__construct($httpUtils, $options);
    public function onAuthenticationSuccess(\Symfony\Component\HttpFoundation\Request $request, \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token)
        $user->setLogged(new \DateTime());
        return $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request));

To make a good “post-login” handler in our case, there are three things of utmost importance.


  1. We must get access to the user object so that we can update the user’s last login time.

  2. We must get access to the entity manager so that the login time can be persisted into the table.

  3. We must get access to the HTTP request so that after the last login time is updated, the app will still be able to redirect us to the “target” URI.

    我们必须获得对HTTP请求的访问权限,以便在更新最后的登录时间后,应用程序仍能够将我们重定向到“目标” URI。

All these are accomplished via the arguments passed to the onAuthenticationSuccess handler’s constructor:


arguments: [@security.http_utils, @service_container, {}]
  1. The user object itself is accessible in the onAuthenticationSuccess method via $token->getUser().


  2. The database entity manager is accessible by the service container passed in (@service_container) and retrieved as $em = $this->container->get('doctrine.orm.entity_manager');.

    可以通过传入的服务容器( @service_container )访问数据库实体管理器,并将其检索为$em = $this->container->get('doctrine.orm.entity_manager'); @service_container

  3. The redirect is done by $this->httpUtils->createRedirectResponse, which will refer to the parameter of @security.http_utils.

    重定向是通过$this->httpUtils->createRedirectResponse ,它将引用@security.http_utils的参数。

Please note that the determineTargetUrl method is called to create a redirect URI based on the $request. Normally, we may visit various URIs: the index page, or a specific link to a post. We can take a look at the implementation of this method in Symfony 2 source (project_root/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Authentication/AuthenticationSuccessHandler.php):

请注意, determineTargetUrl方法被调用基础上,建立一个重定向URI $request 。 通常,我们可能会访问各种URI:索引页面或指向帖子的特定链接。 我们可以在Symfony 2源代码( project_root/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Authentication/AuthenticationSuccessHandler.php )中查看此方法的实现:

protected function determineTargetUrl(Request $request)
        if ($this->options['always_use_default_target_path']) {
            return $this->options['default_target_path'];

        if ($targetUrl = $request->get($this->options['target_path_parameter'], null, true)) {
            return $targetUrl;

        if (null !== $this->providerKey && $targetUrl = $request->getSession()->get('_security.'.$this->providerKey.'.target_path')) {

            return $targetUrl;

        if ($this->options['use_referer'] && ($targetUrl = $request->headers->get('Referer')) && $targetUrl !== $this->httpUtils->generateUri($request, $this->options['login_path'])) {
            return $targetUrl;

        return $this->options['default_target_path'];

It explains the logic on how eventually a target URI (normally the URI that triggers the login) is determined.


结论 (Conclusion)

We just successfully covered two important aspects of application development with Symfony2:


  1. Registration (and Invitation)

  2. Login (and post login)


A recent trend in web sites is using social network credentials (G+, Facebook, etc) to ease the registration/login process. However, a pure in-house registration/login is still of critical importance for some applications. Moreover, understanding the whole flow of this registration/login process helps us understand the security system of Symfony2.

网站上的最新趋势是使用社交网络凭据 (G +,Facebook等)来简化注册/登录过程。 但是,对于某些应用程序而言,纯内部注册/登录仍然至关重要。 此外,了解此注册/登录过程的整个流程有助于我们了解Symfony2的安全系统。

If you’d like to see more content on related points, like validation for example, or just have comments or feedback on this tutorial, please let us know!


翻译自: https://www.sitepoint.com/symfony2-registration-login/


  • 0
  • 0
    觉得还不错? 一键收藏
  • 0


  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助




当前余额3.43前往充值 >
领取后你会自动成为博主和红包主的粉丝 规则
钱包余额 0