You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Phoenix/Elixir搭配Vue前端如何生成csrf_token结合JWT实现用户认证

实现方案

1. 调整路由配置

前后端分离场景下CSRF Token依赖session存储,首先需要给API路由增加session支持,同时新增公开的CSRF Token获取接口:

# lib/auth_tutorial_phoenix_web/router.ex
defmodule AuthTutorialPhoenixWeb.Router do
  use AuthTutorialPhoenixWeb, :router

  # 新增带session支持的API管道
  pipeline :api_with_session do
    plug :accepts, ["json"]
    plug :fetch_session
    plug Plug.CSRFProtection
  end

  scope "/api", AuthTutorialPhoenixWeb do
    pipe_through :api_with_session

    # 新增CSRF Token获取接口
    get "/csrf-token", SessionController, :get_csrf
    post("/users/sign_in", SessionController, :new)
    # 其他需要认证的接口也走该管道
  end
end

2. 实现CSRF Token获取接口

在SessionController中新增接口方法,直接返回生成的CSRF Token:

# lib/auth_tutorial_phoenix_web/controllers/session_controller.ex
def get_csrf(conn, _params) do
  csrf_token = Plug.CSRFProtection.get_csrf_token()
  json(conn, %{csrf_token: csrf_token})
end

3. 调整登录逻辑,将CSRF存入JWT

修改登录方法,接收前端传递的CSRF Token,存入Guardian生成的JWT载荷中,登录成功后同步返回给前端:

# 修改原new方法
def new(conn, %{"email" => email, "password" => password, "csrf_token" => csrf_token}) do
  case Accounts.authenticate_user(email, password) do
    {:ok, user} ->
      # 将csrf_token存入JWT载荷
      {:ok, access_token, _claims} =
        Guardian.encode_and_sign(user, %{csrf: csrf_token}, token_type: "access", ttl: {15, :minute})

      {:ok, refresh_token, _claims} =
        Guardian.encode_and_sign(user, %{csrf: csrf_token}, token_type: "refresh", ttl: {7, :day})

      conn
      |> put_resp_cookie("ruid", refresh_token, http_only: true, same_site: :strict)
      |> put_status(:created)
      # 登录成功后返回csrf_token给前端存储
      |> render("token.json", access_token: access_token, user: user, csrf_token: csrf_token)

    {:error, :unauthorized} ->
      body = Jason.encode!(%{error: "unauthorized"})
      conn
      |> send_resp(401, body)
  end
end

4. 新增CSRF校验Plug

自定义Plug用于后续接口校验,比对请求头中的CSRF Token和JWT中存储的CSRF是否一致:

# lib/auth_tutorial_phoenix_web/plugs/validate_csrf_in_jwt.ex
defmodule AuthTutorialPhoenixWeb.Plugs.ValidateCSRFInJWT do
  import Plug.Conn
  import Guardian.Plug

  def init(opts), do: opts

  def call(conn, _opts) do
    jwt_csrf = current_claims(conn) |> Map.get("csrf")
    request_csrf = get_req_header(conn, "x-csrf-token") |> List.first()

    if jwt_csrf == request_csrf && jwt_csrf != nil do
      conn
    else
      conn
      |> send_resp(403, Jason.encode!(%{error: "无效的CSRF Token"}))
      |> halt()
    end
  end
end

将该Plug加入到需要认证的接口管道中即可生效。

5. 前端Vue适配逻辑

  • 用户进入登录页时,先调用GET /api/csrf-token接口获取CSRF Token,存储到本地
  • 提交登录表单时,将CSRF Token放入请求体或X-CSRF-Token请求头中传递给后端
  • 登录成功后,存储返回的新CSRF Token,后续所有业务请求都将其放入X-CSRF-Token请求头中携带

内容的提问来源于stack exchange,提问作者Cracho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 07:36:03