如何新增「記住我」登入功能
一旦使用者通過驗證,其憑證通常會儲存在 session 中。這表示當 session 結束時,他們將會登出,並且下次希望存取應用程式時必須再次提供登入詳細資訊。您可以使用具有 remember_me
防火牆選項的 cookie,讓使用者選擇保持登入狀態比 session 持續時間更長。
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 並非總是適當的(例如,您不應在共用電腦上使用它)。這就是為什麼預設情況下,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": "dunglas@example.com",
"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 的類別來建立自訂令牌提供者。
然後,將您的自訂令牌提供者的服務 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 將不會與跨網站請求一起傳送,即使在追蹤一般連結時也是如此。