檢視原始程式碼 Phoenix.Token (Phoenix v1.7.14)
方便於在令牌內簽署/加密資料,可用於 Channels、API 驗證等功能。
儲存在令牌中的資料已簽署,可防止資料遭竄改,並可選擇加密。表示只要金鑰(見下文)維持機密,則可以確保令牌中儲存的資料未遭第三方竄改。但除非令牌已加密,否則不建議使用此令牌來儲存私人資訊,例如使用者的敏感識別資料,因為它很容易被解碼。如果令牌已加密,其內容將對用戶端保密,但最佳實務仍建議盡可能只編碼最少的機密資訊,以將金鑰外洩的影響減到最低。
範例
在 API 或 Channel 中產生唯一令牌時,建議使用使用者的唯一識別碼,通常是資料庫中的 id。例如
iex> user_id = 1
iex> token = Phoenix.Token.sign(MyAppWeb.Endpoint, "user auth", user_id)
iex> Phoenix.Token.verify(MyAppWeb.Endpoint, "user auth", token, max_age: 86400)
{:ok, 1}
在該範例中,我們有一個使用者的 id,我們會產生一個令牌並使用給定的 endpoint
中所設定的 secret key base 來驗證它。我們透過設定最大年齡(建議)來確保此令牌只在一天內有效。
sign/4
、verify/4
、encrypt/4
和 decrypt/4
的第一個引數可以是下列之一
- Phoenix 端點的模組名稱(顯示於上文)-其中 secret key base 是從端點中提取
Plug.Conn
-其中 secret key base 是從儲存在連線中的端點中提取Phoenix.Socket
或Phoenix.LiveView.Socket
-其中 secret key base 是從儲存在 Socket 中的端點中提取- 字串,代表 secret key base 本身。應使用至少 20 個隨機產生的字元組成的 key base,以提供足夠的熵
第二個參數為一個 密碼鹽,在呼叫 sign/4
和 verify/4
以及 encrypt/4
和 decrypt/4
時,都必須相同。例如,在產生用於在頻道或 API 上驗證使用者的權杖時,它可以稱為「使用者驗證」並視為命名空間。
第三個參數可以是任何您希望編碼成權杖的項目(字串、整數、清單等)。在經過有效的驗證後,會從權杖中萃取同一個項目。
用法
在簽署權杖後,我們可以用多種方式將它傳送給客戶端。
一種方式為透過 meta 標記
<%= tag :meta, name: "channel_token",
content: Phoenix.Token.sign(@conn, "user auth", @current_user.id) %>
或傳回它的端點
def create(conn, params) do
user = User.create(params)
render(conn, "user.json",
%{token: Phoenix.Token.sign(conn, "user auth", user.id), user: user})
end
在傳送權杖後,客戶端現在可以將它作為驗證機制傳送回伺服器。例如,我們可以使用它在 Phoenix 頻道上驗證使用者
defmodule MyApp.UserSocket do
use Phoenix.Socket
def connect(%{"token" => token}, socket, _connect_info) do
case Phoenix.Token.verify(socket, "user auth", token, max_age: 86400) do
{:ok, user_id} ->
socket = assign(socket, :user, Repo.get!(User, user_id))
{:ok, socket}
{:error, _} ->
:error
end
end
def connect(_params, _socket, _connect_info), do: :error
end
在此範例中,phoenix.js 客戶端會在 connect
命令中傳送權杖,而伺服器會加以驗證。
Phoenix.Token
也可以用於驗證 API、處理密碼重設、電子郵件確認等等。
摘要
類型
@type max_age_opt() :: {:max_age, pos_integer() | :infinity}
@type signed_at_opt() :: {:signed_at, pos_integer()}
函式
@spec decrypt(context(), binary(), binary(), [shared_opt() | max_age_opt()]) :: term()
從權杖解碼原始資料並驗證其完整性。
其用法與 verify/4
相同,但適用於已加密的權杖。
選項
:key_iterations
- 產生加密和簽章金鑰時傳遞給Plug.Crypto.KeyGenerator
的選項。預設為 1000:key_length
- 產生加密和簽章金鑰時傳遞給Plug.Crypto.KeyGenerator
的選項。預設為 32:key_digest
- 產生加密和簽章金鑰時傳遞給Plug.Crypto.KeyGenerator
的選項。預設為:sha256
:max_age
- 僅在「最大期限」秒數前產生 token 的情況下驗證 token。預設為encrypt/4
簽署 token 中的最大期限。
@spec encrypt(context(), binary(), term(), [ shared_opt() | max_age_opt() | signed_at_opt() ]) :: binary()
將資料編碼、加密並簽署成權杖,您可以將它傳送給客戶端。其用法與 sign/4
相同,但會使用 decrypt/4
而不是 verify/4
來萃取資料。
選項
:key_iterations
- 產生加密和簽章金鑰時傳遞給Plug.Crypto.KeyGenerator
的選項。預設為 1000:key_length
- 產生加密和簽章金鑰時傳遞給Plug.Crypto.KeyGenerator
的選項。預設為 32:key_digest
- 產生加密和簽章金鑰時傳遞給Plug.Crypto.KeyGenerator
的選項。預設為:sha256
:signed_at
- 設定 token 的時間戳記(單位:秒)。預設為System.os_time(:millisecond)
:max_age
- token 的預設最大期限。預設為 86,400 秒(1 天),可在decrypt/4
中覆寫。
@spec sign(context(), binary(), term(), [ shared_opt() | max_age_opt() | signed_at_opt() ]) :: binary()
將資料編碼並簽署成權杖,您可以將它傳送給客戶端。
選項
:key_iterations
- 產生加密和簽章金鑰時傳遞給Plug.Crypto.KeyGenerator
的選項。預設為 1000:key_length
- 產生加密和簽章金鑰時傳遞給Plug.Crypto.KeyGenerator
的選項。預設為 32:key_digest
- 產生加密和簽章金鑰時傳遞給Plug.Crypto.KeyGenerator
的選項。預設為:sha256
:signed_at
- 設定 token 的時間戳記(單位:秒)。預設為System.os_time(:millisecond)
:max_age
- token 的預設最大期限。預設為 86,400 秒(1 天),可在verify/4
中覆寫。
@spec verify(context(), binary(), binary(), [shared_opt() | max_age_opt()]) :: {:ok, term()} | {:error, :expired | :invalid | :missing}
從權杖解碼原始資料並驗證其完整性。
範例
在此情境中,我們會建立一個 token,對其進行簽章,然後提供給用戶端應用程式。接著,用戶端會使用這個 token 來驗證對伺服器資源的要求。如需建構 token 的進一步資訊,請參閱 Phoenix.Token
總覽。
iex> user_id = 99
iex> secret = "kjoy3o1zeidquwy1398juxzldjlksahdk3"
iex> namespace = "user auth"
iex> token = Phoenix.Token.sign(secret, namespace, user_id)
將 token 傳遞給用戶端的機制通常是透過 cookie、JSON 回應主體或 HTTP 標頭。目前假設用戶端已收到可用於驗證受保護資源要求的 token。
當伺服器收到要求時,可使用 verify/4
來判斷是否應提供要求的資源給用戶端
iex> Phoenix.Token.verify(secret, namespace, token, max_age: 86400)
{:ok, 99}
在此範例中,我們知道用戶端已傳送有效的 token,因為 verify/4
回傳的型別為 {:ok, user_id}
。伺服器現在可以繼續要求的處理。
但是,如果用戶端已傳送過期的 token、無效的 token 或 nil
,verify/4
會回傳一個錯誤
iex> Phoenix.Token.verify(secret, namespace, expired, max_age: 86400)
{:error, :expired}
iex> Phoenix.Token.verify(secret, namespace, invalid, max_age: 86400)
{:error, :invalid}
iex> Phoenix.Token.verify(secret, namespace, nil, max_age: 86400)
{:error, :missing}
選項
:key_iterations
- 產生加密和簽章金鑰時傳遞給Plug.Crypto.KeyGenerator
的選項。預設為 1000:key_length
- 產生加密和簽章金鑰時傳遞給Plug.Crypto.KeyGenerator
的選項。預設為 32:key_digest
- 產生加密和簽章金鑰時傳遞給Plug.Crypto.KeyGenerator
的選項。預設為:sha256
:max_age
- 僅在「最大期限」秒數前產生 token 的情況下驗證 token。預設為sign/4
簽署 token 中的最大期限。