实现你自己的 SecurityController
网络代理 » 数字新闻 » 实现你自己的 SecurityController

实现你自己的 SecurityController

我最近为我的公司写了一篇关于在本地使用 Symfony2 Core 管理用户及其身份验证的文章,因此没有 FOSUserBundle。 在这篇已经很丰富的第一篇文章之后,我想描述一个同样有用的第二部分,这将使我们能够快速设置基本功能,即重置密码、更改密码、验证帐户甚至注册。 当您有一个管理用户的系统时,操作同样重要。

设置安全控制器

首先,如果您还没有遵循关于如何设置用户管理的第一个教程,我建议您看一看。 如果您按照解决方案进行操作,那么从逻辑上讲,您应该拥有一个 SecurityController 或其他东西。 就我而言,我只有三种方法,其中只有一种是真正可用的。

  1. 登录动作
    此方法连接用户。
  2. 检查动作
    此方法仅允许您为防火墙声明一条路由,允许用户在服务器端进行连接。
  3. 注销动作
    此方法用于为防火墙声明一条允许用户断开连接的路由。

向新用户开放我们的平台

如果我们的用户可以连接并因此能够预先注册,那将特别有趣。

首先,我们生成包含您想询问用户的信息的表单。

关于您将用于注册用户的表格, 知道领域“ 密码 不应该在你的表格中。 但是,您需要添加两个字段“ 未映射 为了让所需的密码输入两次。

1
2
3
4
5
6
7
8
9
10
11
12
13
/ **
 * @方法({“得到”})
 * @路线(“/register”, name=”register”)
 * @安全的(角色=“IS_AUTHENTICATED_ANONYMOUSLY”)
 * @模板()
 */
国家 功能 寄存器()
{
    $表格= $这->创建表格( 用户类型(用户类型::注册), 用户());
    回报 排列(
        “形式” => $表单->createView(),
        );
}

然后,当您的用户填写完表格回来时,我们将不得不注册他或拒绝他的文件:p

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/ **
* @方法({“邮政”})
* @路线(“/登记”)
* @安全的(角色=“IS_AUTHENTICATED_ANONYMOUSLY”)
* @模板()
*/
国家 功能 现在注册(请求$请求)
{
$params = $request->request->all()[“name_of_my_form”];
$表格= $这->创建表格( 用户类型(用户类型::注册), 用户());
$表单->提交($request);
if (array_key_exists(“明文密码”, $params) && array_key_exists(“plain_password2”, $参数) && $参数[“明文密码”] == $参数[“plain_password2”]) {
if ($form->isValid()) {
$data = $form->getData();
$数据->设置密码($这->容器->获取(“security.encoder_factory”)->getEncoder($data)->编码密码($params[“明文密码”], $data->getSalt()));
$em->坚持($数据);
$em->flush();
回报 $这->重定向($这->生成网址(“登录”, 排列(“ 信息 ” => “您已收到一封电子邮件以验证您的帐户。 »)));
}
}
回报 排列(
“错误” => $参数[“明文密码”] == $参数[“plain_password2”]? $表单->getErrors(): 排列(“两个密码必须相同”),
“形式” => $表单->createView(),
);
}

在这里,我们将快速详细介绍 工作流程 注册一个新用户。

  1. 我们看 如果所有必填字段都输入正确,包括两个“密码”字段,如果后两个相同。
  2. 我们对密码进行编码 我们将其“设置”在实体中。
  3. 如果出现任何错误,我们将返回包含我们认为应该详细说明的错误的表格。

创建一个功能来重置您的密码

现在您的用户可以登录了,但是如果他丢失了密码我们该怎么办。 很明显,我们不会设置一个专门用于其无用操作的联系电子邮件地址。

正如您在上面看到的,我通常为我的每个功能声明两个方法:我的一个方法负责管理根据请求创建的视图 的GET 和一个请求的结果 解决方案&帖子. 您完全可以将这两种方法连接成一个相同的方法。

1
2
3
4
5
6
7
8
9
/ **
  * @方法({“得到”})
  * @路线(“/重置”,名称=“重置”)
  * @安全的(角色=“IS_AUTHENTICATED_ANONYMOUSLY”)
  * @模板()
  */
  国家 功能 重置() {
   回报 排列();
  }

在第二步中,我们将声明管理请求的补充方法 解决方案&帖子.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/ **
* @方法({“邮政”})
* @路线(“/重置”)
* @安全的(角色=“IS_AUTHENTICATED_ANONYMOUSLY”)
*/
国家 功能 现在重置(请求$请求)
{
$params = $request->request->all();
if (!array_key_exists(“登录”, $参数)) {
特殊课程(“没有登录”);
}
$登录 = &$参数[“登录”];
$em = $这->容器->获取(“doctrine.orm.default_entity_manager”);
$user = $em->getRepository(“命名空间MyBundle:用户”)->找到一个(排列(“登录” => $登录));
if ($用户 == ){
回报 $这->重定向($这->生成网址(“登录”, 排列()));
}
$密码= “我的随机密码”;
$用户->设置密码($这->容器->获取(“security.encoder_factory”)->getEncoder($user)->encodePassword($password, $user->getSalt()));
$em->persist($user);
$em->flush();
// 我们通过电子邮件发送密码
回报 $这->重定向($这->生成网址(“登录”, 排列()));
}

该方法旨在 重置 提供他的用户的密码 登录/用户名. 在我的例子中,密码是通过电子邮件发送的。 我会让你加上那句豪言壮语。

  1. 所以我们要去 搜索用户.
  2. 我们生成密码 一旦用户根据您定义的规则进行编码,我们就会通知他。

设置密码更改

此时,如果我们的用户丢失了密码,可以生成一个新密码,但如果他只是想更改它,我们需要一个门来定义一个门。

1
2
3
4
5
6
7
8
9
/ **
* @方法({“得到”})
* @路线(“/change”, name=”change-password”)
* @安全的(角色=“IS_AUTHENTICATED_FULLY”)
* @模板()
*/
国家 功能 更改() {
回报 排列();
}

这是生成视图的代码。 首先,您必须输入旧密码,然后输入两次新密码。 第二次是确认。

现在我们将看到代码 重置器 密码。 这 过程 类似于生成新的随机密码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/ **
 * @方法({“邮政”})
 * @路线(“/改变”)
 * @安全的(角色=“IS_AUTHENTICATED_FULLY”)
 * @模板()
 */
国家 功能 现在改变(请求$请求)
{
    $params = $request->request->all();
    if (!array_key_exists(“当前的”, $参数)
        || !array_key_exists(“新的”, $参数)
        || !array_key_exists(“新2”, $参数))
    {
        回报 排列(“错误” => “请填写所有字段”);
    }
    $em = $这->容器->获取(“doctrine.orm.default_entity_manager”);
    $用户= $这->getUser();
    $user_encoders = $这->容器->获取(“security.encoder_factory”)->getEncoder($用户);
    $user_repository = $em->getRepository(“命名空间MyBundle:用户”);
    $current_password_encoded = $user_encoders->encodePassword($params[“当前的”], $user->getSalt());
    $new_password_encoded = $user_encoders->encodePassword($params[“新的”], $user->getSalt());
    if ($user_repository->findOneBy(排列(“密码” => $current_password_encoded)) == ){
        回报 排列(“错误” => “当前密码错误”);
    } elseif ($参数[“新的”] != $参数[“新2”]) {
        回报 排列(“错误” => “两个字段密码不一样”);
    }
    $user->setPassword($new_password_encoded);
    $em->persist($user);
    $em->flush();
    回报 $这->重定向($这->生成网址(“登出”, 排列()));
}

如果你花 1 分钟时间阅读代码,你会发现这个特别简单。

  1. 首先,我们检查三个字段(旧密码、新密码和确认)是否输入正确。
  2. On 输入密码 当前和 我们将其与当前密码进行比较在数据库中修改以查看它是否与输入的旧密码相对应。
  3. 我们检查“两个”新密码是否是 相同.
  4. 输入新密码并 在实体中。

激活他的帐户

此功能在其他方面没有完美详述 片段 多于。 它的目的是取消阻止刚刚注册的用户,例如,当他验证了他的电子邮件时。 出于多种原因,几乎所有我们知道的平台都开发了此功能。 要设置对用户的阻止,您可能还需要实施提供程序。

  • /限制帐户 等乐 垃圾邮件.
  • VERIFIER 用户填写的电子邮件地址乍一看似乎可用。
  • 删除 一段时间后尚未验证的帐户。

工作流程

  1. 用户注册。 然后,他的帐户会通过一个特定于您的字段被阻止。 只要该字段指示该帐户被禁用,该字段就会阻止他连接。
1
2
3
4
5
6
7
8
// 命名空间MyBundleEntityUser
用户 {
国家 功能 __构造() {
$这->token =散列(“sha512”, 唯一标识符());
}
...
}
1
$用户->设置启用(false);
  1. 用户在他的个人资料被删除时收到了一封电子邮件 齐平 在数据库中。 此电子邮件必须是您生成的地址的一部分。
    在这条路上,一个 token 或必须提供唯一标识符,以便找到相关用户。 我建议您使用旨在随机的 UUID4。 您可以找到 UUID 列表以及所有版本的描述。
1
2
3
4
/ **
* @路线(“/激活”,名称=“激活”)
*/
国家 功能 激活() {…}
1
$这->生成网址(“启用”, 排列(« token » => $用户->得到Token()), true);

你应该有一个像这样的 URL。

1
HTTP://主机名/激活?token=我的唯一Token
  1. 用户打开他的电子邮件并尝试通过单击提供的链接来激活他的帐户。 下面我们进入流程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/ **
 * @方法({“得到”})
 * @路线(“/激活”,名称=“激活”)
 * @安全的(角色=“IS_AUTHENTICATED_ANONYMOUSLY”)
 * @模板()
 */
国家 功能 激活(请求$请求) {
    $参数= 排列();
    $token = $请求->查询->获取(« token »);
    $em = $这->容器->获取(“doctrine.orm.default_entity_manager”);
$user = $em->getRepository(“命名空间MyBundle:用户”)->找到一个(排列(« token » => $token));
    if ($用户!= ){
        $用户->设置启用(true);
        $em->persist($user);
        $em->flush();
        $参数[“启用”] = true;
    } 其他 {
        $参数[“启用”] = false;
    }
    回报 $参数;
}

通过此过程,您应该可以毫无问题地启用用户帐户验证。

您可以在此 Gist 上找到等效代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
<?php
使用 JMS安全额外捆绑注解安全;
使用 SENSIO种子套餐框架ExtraBundle配置付款方式;
使用 SENSIO种子套餐框架ExtraBundle配置路线;
使用 SENSIO种子套餐框架ExtraBundle配置模板;
使用 Symfony的种子套餐框架包控制器控制器;
使用 Symfony的元件Http基金会询问;
使用 Symfony的元件严格安保核心安全上下文;
安全控制器 扩展 控制器
{
/ **
* @方法({“得到”})
* @路线(“/登录”,名称=“登录”)
* @模板()
*/
国家 功能 登录(请求$请求)
{
$请求= $这->getRequest();
$session = $request->getSession();
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} 其他 {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
}
$参数= 排列(
“last_username” => $session->get(SecurityContext::LAST_USERNAME),
“错误” => $错误,
“ 信息 ” => $请求->得到(“ 信息 ”),
);
if ($request->isXmlHttpRequest()) {
回报 $这->渲染(“GCDirectoryMainBundle:安全:login-ajax.html.twig”, $参数);
}
回报 $参数;
}
/ **
* @方法({“邮政”})
* @路线(“/login_check”, name=”login_check”)
*/
国家 功能 ()
{
RuntimeException(“您必须在安全防火墙配置中使用 form_login 配置要由防火墙处理的检查路径。”);
}
/ **
* @方法({“得到”})
* @路线(“/注销”,名称=“注销”)
*/
国家 功能 注销()
{
RuntimeException(“您必须在您的安全防火墙配置中激活注销。”);
}
/ **
* @方法({“得到”})
* @路线(“/重置”,名称=“重置”)
* @安全的(角色=“IS_AUTHENTICATED_ANONYMOUSLY”)
* @模板()
*/
国家 功能 重置() {
回报 排列();
}
/ **
* @方法({“邮政”})
* @路线(“/重置”)
* @安全的(角色=“IS_AUTHENTICATED_ANONYMOUSLY”)
*/
国家 功能 现在重置(请求$请求)
{
$params = $request->request->all();
if (!array_key_exists(“登录”, $参数)) {
特殊课程(“没有登录”);
}
$登录 = &$参数[“登录”];
$em = $这->容器->获取(“doctrine.orm.default_entity_manager”);
$user = $em->getRepository(“命名空间MyBundle:用户”)->找到一个(排列(“登录” => $登录));
if ($用户 == ){
回报 $这->重定向($这->生成网址(“登录”, 排列()));
}
$密码= “我的随机密码”;
$用户->设置密码($这->容器->获取(“security.encoder_factory”)->getEncoder($user)->encodePassword($password, $user->getSalt()));
$em->persist($user);
$em->flush();
回报 $这->重定向($这->生成网址(“登录”, 排列()));
}
/ **
* @方法({“得到”})
* @路线(“/change”, name=”change-password”)
* @安全的(角色=“IS_AUTHENTICATED_FULLY”)
* @模板()
*/
国家 功能 更改() {
回报 排列();
}
/ **
* @方法({“邮政”})
* @路线(“/改变”)
* @安全的(角色=“IS_AUTHENTICATED_FULLY”)
* @模板()
*/
国家 功能 现在改变(请求$请求)
{
$params = $request->request->all();
if (!array_key_exists(“当前的”, $参数)
|| !array_key_exists(“新的”, $参数)
|| !array_key_exists(“新2”, $参数))
{
回报 排列(“错误” => “请填写所有字段”);
}
$em = $这->容器->获取(“doctrine.orm.default_entity_manager”);
$用户= $这->getUser();
$user_encoders = $这->容器->获取(“security.encoder_factory”)->getEncoder($用户);
$user_repository = $em->getRepository(“命名空间MyBundle:用户”);
$current_password_encoded = $user_encoders->encodePassword($params[“当前的”], $user->getSalt());
$new_password_encoded = $user_encoders->encodePassword($params[“新的”], $user->getSalt());
if ($user_repository->findOneBy(排列(“密码” => $current_password_encoded)) == ){
回报 排列(“错误” => “当前密码错误”);
} elseif ($参数[“新的”] != $参数[“新2”]) {
回报 排列(“错误” => “两个字段密码不一样”);
}
$user->setPassword($new_password_encoded);
$em->persist($user);
$em->flush();
回报 $这->重定向($这->生成网址(“登出”, 排列()));
}
/ **
* @方法({“得到”})
* @路线(“/register”, name=”register”)
* @安全的(角色=“IS_AUTHENTICATED_ANONYMOUSLY”)
* @模板()
*/
国家 功能 寄存器()
{
$表格= $这->创建表格( 用户类型(用户类型::注册), 用户());
回报 排列(
“形式” => $表单->createView(),
);
}
/ **
* @方法({“邮政”})
* @路线(“/登记”)
* @安全的(角色=“IS_AUTHENTICATED_ANONYMOUSLY”)
* @模板()
*/
国家 功能 现在注册(请求$请求)
{
$params = $request->request->all()[“name_of_my_form”];
$表格= $这->创建表格( 用户类型(用户类型::注册), 用户());
$表单->提交($request);
if (array_key_exists(“明文密码”, $params) && array_key_exists(“plain_password2”, $参数) && $参数[“明文密码”] == $参数[“plain_password2”]) {
if ($form->isValid()) {
$data = $form->getData();
$数据->设置密码($这->容器->获取(“security.encoder_factory”)->getEncoder($data)->编码密码($params[“明文密码”], $data->getSalt()));
$em->坚持($数据);
$em->flush();
回报 $这->重定向($这->生成网址(“登录”, 排列(“ 信息 ” => “您已收到一封电子邮件以验证您的帐户。 »)));
}
}
回报 排列(
“错误” => $参数[“明文密码”] == $参数[“plain_password2”]? $表单->getErrors(): 排列(“两个密码必须相同”),
“形式” => $表单->createView(),
);
}
/ **
* @方法({“得到”})
* @路线(“/激活”,名称=“激活”)
* @安全的(角色=“IS_AUTHENTICATED_ANONYMOUSLY”)
* @模板()
*/
国家 功能 激活(请求$请求) {
$参数= 排列();
$token = $请求->查询->获取(« token »);
$em = $这->容器->获取(“doctrine.orm.default_entity_manager”);
$user = $em->getRepository(“命名空间MyBundle:用户”)->找到一个(排列(« token » => $token));
if ($用户!= ){
$user->setActive(User::ACTIVE_ACTIVE);
$em->persist($user);
$em->flush();
$参数[“启用”] = true;
} 其他 {
$参数[“启用”] = false;
}
回报 $参数;
}
}

★★★★★