檢視原始程式碼 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/4verify/4encrypt/4decrypt/4 的第一個引數可以是下列之一

  • Phoenix 端點的模組名稱(顯示於上文)-其中 secret key base 是從端點中提取
  • Plug.Conn -其中 secret key base 是從儲存在連線中的端點中提取
  • Phoenix.SocketPhoenix.LiveView.Socket -其中 secret key base 是從儲存在 Socket 中的端點中提取
  • 字串,代表 secret key base 本身。應使用至少 20 個隨機產生的字元組成的 key base,以提供足夠的熵

第二個參數為一個 密碼鹽,在呼叫 sign/4verify/4 以及 encrypt/4decrypt/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、處理密碼重設、電子郵件確認等等。

摘要

函式

從權杖解碼原始資料並驗證其完整性。

將資料編碼、加密並簽署成權杖,您可以將它傳送給客戶端。其用法與 sign/4 相同,但會使用 decrypt/4 而不是 verify/4 來萃取資料。

將資料編碼並簽署成權杖,您可以將它傳送給客戶端。

從權杖解碼原始資料並驗證其完整性。

類型

@type context() ::
  Plug.Conn.t()
  | %{:endpoint => atom(), optional(atom()) => any()}
  | atom()
  | binary()
@type max_age_opt() :: {:max_age, pos_integer() | :infinity}
@type shared_opt() ::
  {:key_iterations, pos_integer()}
  | {:key_length, pos_integer()}
  | {:key_digest, :sha256 | :sha384 | :sha512}
@type signed_at_opt() :: {:signed_at, pos_integer()}

函式

連結至此函式

decrypt(context, secret, token, opts \\ [])

檢視來源
@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 中的最大期限。
連結至此函式

encrypt(context, secret, data, opts \\ [])

檢視來源
@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 中覆寫。
連結至此函式

sign(context, salt, data, opts \\ [])

檢視來源
@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 中覆寫。
連結至此函式

verify(context, salt, token, opts \\ [])

檢視來源
@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 或 nilverify/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 中的最大期限。