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

## 表单，数据库等 (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.

This class (RegistrationType) is defined in 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).

{
$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. 而已。 用户现在可以输入用户名和密码并登录。 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):

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

### 结论 (Conclusion)

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

1. Registration (and Invitation)

注册(和邀请)

登录(和发布登录)

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.

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!

yii2和symfony

06-04 258
03-24 8551
08-13 2188
04-15 83
07-21 842
09-22 877

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