跳到主要內容

如何新增「記住我」登入功能

編輯此頁面

一旦使用者通過驗證,其憑證通常會儲存在 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 系統後,還有幾件事要做才能使「記住我」功能正常運作

  1. 新增一個選擇加入的核取方塊以啟用「記住我」;
  2. 使用支援「記住我」的驗證器;
  3. 或者,設定「記住我」cookie 的儲存和驗證方式

在此之後,成功驗證時將會建立「記住我」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 來指示支援。

登入後,您可以使用安全性分析器來查看此徽章是否存在

The Security page of the Symfony profiler, with the "Authenticators" tab showing the remember me badge in the passport object.

如果沒有此徽章,則不會啟用「記住我」(無論所有其他設定如何)。

為自訂驗證器新增「記住我」支援

當您使用自訂驗證器時,您必須手動新增 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 的*雜湊值*。此雜湊值是根據設定的簽名屬性計算得出的。

這些屬性始終包含在雜湊值中

除了這些之外,您可以使用 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 屬性,僅當使用者透過「記住我」機制驗證時才授予存取權。

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 將不會與跨網站請求一起傳送,即使在追蹤一般連結時也是如此。
本作品,包括程式碼範例,皆以創用 CC BY-SA 3.0 授權條款授權。
目錄
    版本