如何添加“记住我”登录功能
一旦用户通过身份验证,他们的凭据通常会存储在会话中。这意味着当会话结束时,他们将被注销,并且下次希望访问应用程序时必须再次提供登录详细信息。您可以使用带有 remember_me
防火墙选项的 Cookie,允许用户选择保持登录状态的时间比会话持续时间更长
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# config/packages/security.yaml
security:
# ...
firewalls:
main:
# ...
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 1 week in seconds
# by default, the feature is enabled by checking a
# checkbox in the login form (see below), uncomment the
# following line to always enable it.
#always_remember_me: true
7.2
从 Symfony 7.2 开始,不再需要 secret
选项。默认情况下,使用 %kernel.secret%
,它使用 APP_SECRET
环境变量定义。
在配置中启用 remember_me
系统后,还有几件事要做才能使记住我功能正常工作
完成这些操作后,记住我 Cookie 将在成功验证身份后创建。对于某些页面/操作,您可以强制用户完全验证身份(即,不是通过记住我 Cookie 验证),以获得更好的安全性。
注意
remember_me
设置包含许多用于配置此功能创建的 Cookie 的设置。有关这些设置的完整描述,请参阅自定义记住我 Cookie。
激活记住我系统
使用记住我 Cookie 并非总是合适的(例如,您不应在共享 PC 上使用它)。这就是为什么默认情况下,Symfony 要求您的用户通过请求参数选择加入记住我系统的原因。
表单登录的记住我
此请求参数通常通过登录表单中的复选框设置。此复选框的名称必须为 _remember_me
1 2 3 4 5 6 7 8 9 10 11
{# templates/security/login.html.twig #}
<form method="post">
{# ... your form fields #}
<label>
<input type="checkbox" name="_remember_me" checked>
Keep me logged in
</label>
{# ... #}
</form>
注意
或者,您可以使用 remember_me
部分下的 name
设置为此复选框配置自定义名称。
JSON 登录的记住我
如果您通过使用JSON 登录的 API 实现登录,则可以将 _remember_me
键添加到 POST 请求的正文中。
1 2 3 4 5
{
"username": "[email protected]",
"password": "MyPassword",
"_remember_me": true
}
注意
或者,您可以使用防火墙的 remember_me
部分下的 name
设置为此键配置自定义名称。
始终激活记住我
有时,您可能希望始终激活记住我系统,而不允许用户选择退出。在这些情况下,您可以使用 always_remember_me
设置
1 2 3 4 5 6 7 8 9 10
# config/packages/security.yaml
security:
# ...
firewalls:
main:
# ...
remember_me:
# ...
always_remember_me: true
现在,不会检查任何请求参数,并且每次成功的身份验证都会生成一个记住我 Cookie。
向认证器添加记住我支持
并非所有身份验证方法都支持记住我(例如,HTTP Basic 身份验证不支持)。认证器使用 安全通行证上的 RememberMeBadge
来指示支持。
登录后,您可以使用安全分析器来查看此徽章是否存在

如果没有此徽章,则记住我功能将不会被激活(无论所有其他设置如何)。
向自定义认证器添加记住我支持
当您使用自定义认证器时,必须手动添加 RememberMeBadge
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// src/Service/LoginAuthenticator.php
namespace App\Service;
// ...
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
class LoginAuthenticator extends AbstractAuthenticator
{
public function authenticate(Request $request): Passport
{
// ...
return new Passport(
new UserBadge(...),
new PasswordCredentials(...),
[
new RememberMeBadge(),
]
);
}
}
自定义记住我令牌的存储方式
记住我 Cookie 包含一个令牌,该令牌用于验证用户身份。由于这些令牌是长期存在的,因此采取预防措施以使任何生成的令牌失效非常重要。
Symfony 提供了两种验证记住我令牌的方法
- 基于签名的令牌
- 默认情况下,记住我 Cookie 包含基于用户属性的签名。如果属性发生更改,则签名也会更改,并且已生成的令牌不再被视为有效。有关更多信息,请参阅如何使用它们。
- 持久令牌
- 持久令牌存储任何生成的令牌(例如,在数据库中)。这允许您通过更改数据库中的行来使令牌失效。有关如何存储令牌的更多信息,请参阅如何存储令牌。
注意
您还可以通过创建一个扩展 AbstractRememberMeHandler(或实现 RememberMeHandlerInterface)的类来编写自己的自定义记住我处理程序。然后,您可以通过在 remember_me
下的 service
选项中配置服务 ID 来配置此自定义处理程序。
使用签名记住我令牌
默认情况下,记住我 Cookie 包含一个哈希值,该哈希值用于验证 Cookie。此哈希值基于配置的签名属性计算得出。
这些属性始终包含在哈希值中
- 用户标识符(由 getUserIdentifier() 返回);
- 到期时间戳。
最重要的是,您可以使用 signature_properties
设置配置自定义属性(默认为 password
)。这些属性使用 PropertyAccess 组件从用户对象中获取(例如,当使用 updatedAt
时,使用 getUpdatedAt()
或公共 $updatedAt
属性)。
1 2 3 4 5 6 7 8 9 10
# config/packages/security.yaml
security:
# ...
firewalls:
main:
# ...
remember_me:
# ...
signature_properties: ['password', 'updatedAt']
在此示例中,如果此用户的 updatedAt
、密码或用户标识符发生更改,则记住我 Cookie 将不再被视为有效。
提示
签名属性允许一些高级用法,而无需为所有记住我令牌设置存储。例如,您可以向您的用户和签名属性添加 forceReloginAt
字段。这样,您可以通过更改此时间戳来使来自用户的所有记住我令牌失效。
在数据库中存储记住我令牌
由于记住我令牌通常是长期存在的,您可能更喜欢将它们保存在数据库中以便完全控制它们。Symfony 提供了对持久记住我令牌的支持。
此实现使用记住我令牌提供程序来存储和检索数据库中的令牌。DoctrineBridge 提供了一个使用 Doctrine 的令牌提供程序。
您可以使用 doctrine
设置启用 doctrine 令牌提供程序
1 2 3 4 5 6 7 8 9 10 11
# config/packages/security.yaml
security:
# ...
firewalls:
main:
# ...
remember_me:
# ...
token_provider:
doctrine: true
这也指示 Doctrine 为记住我令牌创建一个表。如果您使用 DoctrineMigrationsBundle,您可以为此创建一个新的迁移
1 2 3 4
$ php bin/console doctrine:migrations:diff
# and optionally run the migrations locally
$ php bin/console doctrine:migrations:migrate
否则,您可以使用 doctrine:schema:update
命令
1 2 3 4 5
# get the required SQL code
$ php bin/console doctrine:schema:update --dump-sql
# run the SQL in your DB client, or let the command run it for you
$ php bin/console doctrine:schema:update --force
实现自定义令牌提供程序
您还可以通过创建一个实现 TokenProviderInterface 的类来创建自定义令牌提供程序。
然后,将您的自定义令牌提供程序的 Service ID 配置为 service
1 2 3 4 5 6 7 8 9 10 11
# config/packages/security.yaml
security:
# ...
firewalls:
main:
# ...
remember_me:
# ...
token_provider:
service: App\Security\RememberMe\CustomTokenProvider
强制用户在访问特定资源前重新验证身份
当用户返回您的站点时,他们会根据记住我 Cookie 中存储的信息自动进行身份验证。这允许用户访问受保护的资源,就好像用户在访问站点时实际已通过身份验证一样。
但是,在某些情况下,您可能希望强制用户在访问某些资源之前实际重新验证身份。例如,您可能不允许“记住我”用户更改其密码。您可以通过利用一些特殊的“属性”来做到这一点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// src/Controller/AccountController.php
// ...
public function accountInfo(): Response
{
// allow any authenticated user - we don't care if they just
// logged in, or are logged in via a remember me cookie
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED');
// ...
}
public function resetPassword(): Response
{
// require the user to log in during *this* session
// if they were only logged in via a remember me cookie, they
// will be redirected to the login page
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
// ...
}
提示
还有一个 IS_REMEMBERED
属性,该属性仅在用户通过记住我机制进行身份验证时授予访问权限。
自定义记住我 Cookie
remember_me
配置包含许多选项,用于自定义系统创建的 Cookie
name
(默认值:REMEMBERME
)- 用于保持用户登录状态的 Cookie 的名称。如果您在同一应用程序的多个防火墙中启用了
remember_me
功能,请确保为每个防火墙的 Cookie 选择不同的名称。否则,您将面临许多与安全相关的问题。 lifetime
(默认值:31536000
,即 1 年,以秒为单位)- Cookie 过期前的秒数。这定义了用户保持身份验证状态的两次访问之间的最大时间。
path
(默认值:/
)- 与此功能关联的 Cookie 使用的路径。默认情况下,Cookie 将应用于整个网站,但您可以限制为特定部分(例如
/forum
,/admin
)。 domain
(默认值:null
)- 与此功能关联的 Cookie 使用的域。默认情况下,Cookie 使用从
$_SERVER
获取的当前域。 secure
(默认值:false
)- 如果为
true
,则与此功能关联的 Cookie 将通过 HTTPS 安全连接发送给用户。 httponly
(默认值:true
)- 如果为
true
,则与此功能关联的 Cookie 只能通过 HTTP 协议访问。这意味着 Cookie 将无法通过脚本语言(例如 JavaScript)访问。 samesite
(默认值:null
)- 如果设置为
strict
,则与此功能关联的 Cookie 将不会与跨站点请求一起发送,即使在遵循常规链接时也是如此。