基于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
相关产品推荐
相关产品推荐

