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

基于Reagent+Ring的全栈Clojure应用POST请求遇403反伪造令牌无效问题

解决Clojure全栈应用POST请求403 Invalid anti-forgery token问题

问题原因

你的后端使用了site-defaults中间件,其中默认集成了ring-anti-forgery的校验逻辑:所有POST请求必须携带有效的anti-forgery token,否则会返回403错误。当前前端的fetch请求未传递该token,因此触发校验失败。

解决方案

方案1:正确传递Anti-Forgery Token(推荐,保障安全)

步骤1:后端在页面中注入Token

修改你的index-handler,在返回的HTML中添加包含anti-forgery token的meta标签:

(defn index-handler [req]
  (r/response
   (str "<!DOCTYPE html>"
        "<html>"
        "<head>"
        "<meta name='csrf-token' content='" (:anti-forgery-token req) "'>"
        ;; 其他页面头部内容
        "</head>"
        "<body>"
        "<div id='app'></div>"
        "<script src='/js/main.js'></script>"
        "</body>"
        "</html>")))

步骤2:前端请求时携带Token

修改ClojureScript的fetch请求,从meta标签获取token并添加到请求头:

(defn trigger-page []
  (let [get-csrf-token (fn []
                         (-> (.querySelector js/document "meta[name='csrf-token']")
                             (.getAttribute "content")))
        do-the-thing (fn []
                       (promesa/let [csrf-token (get-csrf-token)
                                     _response (js/fetch "api/trigger/"
                                                          (clj->js {:headers {"Content-Type" "application/json"
                                                                              "X-CSRF-Token" csrf-token}
                                                                   :method "POST"
                                                                   :body (js/JSON.stringify #js {:args "TEST"})}))
                                     response (.json _response)
                                     data (js->clj response :keywordize-keys true)]
                         (js/console.log response)
                         data))]
    (fn []
      [:span.main
       [:h1 "Trigger"]
       [:button {:on-click #(do-the-thing)} "Trigger"]
       [:ul]])))

方案2:为API端点关闭Anti-Forgery校验(适合无会话的纯API场景)

如果api/trigger是无需会话认证的纯API接口,可以单独为该路由禁用anti-forgery中间件:

;; 先确保导入api-defaults
(ns your-app.core
  (:require [ring.middleware.defaults :refer [site-defaults api-defaults]]
            ;; 其他依赖
            ))

(def app
  (reitit-ring/ring-handler
   (reitit-ring/router
    [["/" {:get {:handler index-handler}}]
     ["/trigger" {:get {:handler index-handler}}]
     ["api/"
      ["trigger/" {:post {:handler do-the-stuff
                          ;; 用api-defaults替代site-defaults,不含anti-forgery校验
                          :middleware [#(wrap-defaults % api-defaults)]}}]]
     ["/about" {:get {:handler index-handler}}]])
   (reitit-ring/routes
    (reitit-ring/create-resource-handler {:path "/" :root "/public"})
    (reitit-ring/create-default-handler))
   {:middleware middleware}))

为什么直接引入ring-anti-forgery没解决问题?

因为site-defaults已经内置了anti-forgery中间件,重复引入不会改变逻辑——核心问题是前端未传递正确的token,而非中间件未加载。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 22:13:11