如何在特定的 reitit 路由上应用环防伪造?
How to apply ring-anti-forgery on specific reitit routes?
在包装使用 metosin/reitit reitit.ring/ring-router
. I've also tried reitit's middleware registry 创建的特定路由时,我一直收到 "Invalid anti-forgery token",但它也不起作用。虽然我可以用 wrap-session
和 wrap-anti-forgery
包装整个处理程序,但这破坏了 reitit 在允许特定于路由的中间件方面的优势。
(ns t.core
(:require [immutant.web :as web]
[reitit.ring :as ring]
[ring.middleware.anti-forgery :refer [wrap-anti-forgery]]
[ring.middleware.content-type :refer [wrap-content-type]]
[ring.middleware.params :refer [wrap-params]]
[ring.middleware.keyword-params :refer [wrap-keyword-params]]
[ring.middleware.session :refer [wrap-session]]
[ring.util.anti-forgery :refer [anti-forgery-field]]
[ring.util.response :as res]))
(defn render-index [_req]
(res/response (str "<form action='/sign-in' method='post'>"
(anti-forgery-field)
"<button>Sign In</button></form>")))
(defn sign-in [{:keys [params session]}]
(println "params: " params
"session:" session)
(res/redirect "/index.html"))
(defn wrap-af [handler]
(-> handler
wrap-anti-forgery
wrap-session
wrap-keyword-params
wrap-params))
(def app
(ring/ring-handler
(ring/router [["/index.html" {:get render-index
:middleware [[wrap-content-type]
[wrap-af]]}]
["/sign-in" {:post sign-in
:middleware [wrap-af]}]])))
(defn -main [& args]
(web/run app {:host "localhost" :port 7777}))
原来是metosin/reitit creates one session store for each route (refer to issue 205获取更多信息);换句话说,ring-anti-forgery 不起作用,因为 reitit 不会为每条路由使用相同的会话存储。
在回答这个问题时,维护者提出了以下建议(为了便于在 Stack Overflow 中参考,从问题中复制):
- 在路由器外部安装 wrap-session,因此整个应用程序只有一个 mw 实例。为此,ring-handler 中有 :middleware 选项:
(require '[reitit.ring :as ring])
(require '[ring.middleware.session :as session])
(defn handler [{session :session}]
(let [counter (inc (:counter session 0))]
{:status 200
:body {:counter counter}
:session {:counter counter}}))
(def app
(ring/ring-handler
(ring/router
["/api"
["/ping" handler]
["/pong" handler]])
(ring/create-default-handler)
;; the middleware on ring-handler runs before routing
{:middleware [session/wrap-session]}))
- 创建单个会话存储并在路由中使用它 table(会话中间件的所有实例将共享单个存储)。
(require '[ring.middleware.session.memory :as memory])
;; single instance
(def store (memory/memory-store))
;; inside, with shared store
(def app
(ring/ring-handler
(ring/router
["/api"
{:middleware [[session/wrap-session {:store store}]]}
["/ping" handler]
["/pong" handler]])))
此答案中未显示维护者要求 PR 的第三个选项。
在包装使用 metosin/reitit reitit.ring/ring-router
. I've also tried reitit's middleware registry 创建的特定路由时,我一直收到 "Invalid anti-forgery token",但它也不起作用。虽然我可以用 wrap-session
和 wrap-anti-forgery
包装整个处理程序,但这破坏了 reitit 在允许特定于路由的中间件方面的优势。
(ns t.core
(:require [immutant.web :as web]
[reitit.ring :as ring]
[ring.middleware.anti-forgery :refer [wrap-anti-forgery]]
[ring.middleware.content-type :refer [wrap-content-type]]
[ring.middleware.params :refer [wrap-params]]
[ring.middleware.keyword-params :refer [wrap-keyword-params]]
[ring.middleware.session :refer [wrap-session]]
[ring.util.anti-forgery :refer [anti-forgery-field]]
[ring.util.response :as res]))
(defn render-index [_req]
(res/response (str "<form action='/sign-in' method='post'>"
(anti-forgery-field)
"<button>Sign In</button></form>")))
(defn sign-in [{:keys [params session]}]
(println "params: " params
"session:" session)
(res/redirect "/index.html"))
(defn wrap-af [handler]
(-> handler
wrap-anti-forgery
wrap-session
wrap-keyword-params
wrap-params))
(def app
(ring/ring-handler
(ring/router [["/index.html" {:get render-index
:middleware [[wrap-content-type]
[wrap-af]]}]
["/sign-in" {:post sign-in
:middleware [wrap-af]}]])))
(defn -main [& args]
(web/run app {:host "localhost" :port 7777}))
原来是metosin/reitit creates one session store for each route (refer to issue 205获取更多信息);换句话说,ring-anti-forgery 不起作用,因为 reitit 不会为每条路由使用相同的会话存储。
在回答这个问题时,维护者提出了以下建议(为了便于在 Stack Overflow 中参考,从问题中复制):
- 在路由器外部安装 wrap-session,因此整个应用程序只有一个 mw 实例。为此,ring-handler 中有 :middleware 选项:
(require '[reitit.ring :as ring])
(require '[ring.middleware.session :as session])
(defn handler [{session :session}]
(let [counter (inc (:counter session 0))]
{:status 200
:body {:counter counter}
:session {:counter counter}}))
(def app
(ring/ring-handler
(ring/router
["/api"
["/ping" handler]
["/pong" handler]])
(ring/create-default-handler)
;; the middleware on ring-handler runs before routing
{:middleware [session/wrap-session]}))
- 创建单个会话存储并在路由中使用它 table(会话中间件的所有实例将共享单个存储)。
(require '[ring.middleware.session.memory :as memory])
;; single instance
(def store (memory/memory-store))
;; inside, with shared store
(def app
(ring/ring-handler
(ring/router
["/api"
{:middleware [[session/wrap-session {:store store}]]}
["/ping" handler]
["/pong" handler]])))
此答案中未显示维护者要求 PR 的第三个选项。