yii2和symfony_Symfony2注册和登录

yii2和symfony

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.

我们还知道用户对象最终将保留在user表中。

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)
    {
        $resolver->setDefaults([
            '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">
        <label>
            <input type="checkbox" value="remember-me" required checked>Disclaimer
        </label>
    </div>
    <button class="btn btn-lg btn-primary btn-block" type="submit">Register</button>
</form>

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());
        $form->handleRequest($req);

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

        $user->setCreated(new \DateTime());
        $user->setRoles('ROLE_USER');
        $user->setGravatar('http://www.gravatar.com/avatar/'.md5(trim($req->get('email'))));
        $user->setActive(true);

        $pwd=$user->getPassword();
        $encoder=$this->container->get('security.password_encoder');
        $pwd=$encoder->encodePassword($user, $pwd);
        $user->setPassword($pwd);
        
        $em->persist($user);
        $em->flush();

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

    $this->createForm将再次被调用以生成一个基于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:

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

login:
    path: /login
    defaults: { _controller: AppBundle:Security:login}    
        
login_check:
    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">
    	<label>
        	<input type="checkbox" value="remember-me" required checked>Disclaimer
        </label>
    </div>
    <button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>
</form>

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

    在控制器中,我们可以使用$this->getUser()获取当前用户的信息(以User对象形式的user记录)。

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

    在Twig中,我们可以使用某些辅助函数来访问用户对象。

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的安全控制界面没有提供允许应用程序执行某些登录后操作的直观方法。 没有这样的事情:

after_login:
	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:

首先,我们在service.yml注册一个服务(“成功登录后”):

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

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

接下来,我们创建一个src/AppBundle/Handler/AuthenticationHandler.php文件:

class AuthenticationSuccessHandler extends DefaultAuthenticationSuccessHandler
{
    protected $container;
    
    public function __construct(HttpUtils $httpUtils, \Symfony\Component\DependencyInjection\ContainerInterface $cont, array $options)
    {
        parent::__construct($httpUtils, $options);
        $this->container=$cont;
    }
    
    public function onAuthenticationSuccess(\Symfony\Component\HttpFoundation\Request $request, \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token)
    {
        $user=$token->getUser();
        $user->setLogged(new \DateTime());
        
        $em=$this->container->get('doctrine.orm.entity_manager');
        
        $em->persist($user);
        $em->flush();
        
        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:

所有这些都是通过传递给onAuthenticationSuccess处理程序的构造函数的参数完成的:

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

    用户对象本身是在可访问的onAuthenticationSuccess经由方法$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')) {
            $request->getSession()->remove('_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.

它解释了如何最终确定目标URI(通常是触发登录的URI)的逻辑。

结论 (Conclusion)

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

我们刚刚通过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/

yii2和symfony

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值