如何在 Ring 应用中验证路由的子集?

3
我有两组compojure路由,公共路由不需要认证,私有路由需要认证。
(defroutes public-routes
  (GET "/" [] homepage-handler))

(defroutes private-routes
  (GET "/secrets" [] secrets-handler))

我创建了一个中间件,用于检查用户是否已经通过身份验证,并继续执行中间件链或者引发异常。
(defn wrap-must-be-authenticated [handler]
  (fn [request]
    (if (authenticated? request)
      (handler request)
      (throw-unauthorized))))

(def app
  (-> private-routes
      (wrap-must-be-authenticated)))

这很好,所有的“私有路由”都需要身份验证。

我该如何添加public-routes,使它们从wrap-must-be-authenticated中排除?

我认为defroutes返回环形处理程序,所以我想我需要这样做:

(-> (wrap-must-be-authenticated private-routes)
     public-routes)
1个回答

4

有一种方法是将多个routes定义放在一个包含的routes中,并使用中间件包装(wrap-routes)适当的路由以限制访问:

(def all-routes
  (routes
    (-> #'private-routes
        (wrap-routes wrap-must-be-authenticated))

    #'public-routes

    (route/not-found
      (:body
        (error-page {:status 404
                     :title "page not found"})))))

以下是我在使用buddy.auth的restrict时的另一个示例:

(defn wrap-admin [handler]
  (restrict handler {:handler (fn [req]
                                (boolean (get-in req [:session :admin?])))}))

(def app-routes
  (routes
    (-> #'admin-routes
        (wrap-routes wrap-admin)
        (wrap-routes middleware/wrap-csrf)
        (wrap-routes middleware/wrap-formats))
    (-> #'home-routes
        (wrap-routes middleware/wrap-csrf)
        (wrap-routes middleware/wrap-formats))))

谢谢 - #' 是什么意思? - Kris
2
使用 var 来定义路由,这可以帮助你在运行应用时编辑/重新加载代码。路由不会绑定到静态值,而是从 var 中解析,这个值可以在应用启动后进行更改。顺便提一下,您不必在路由前加上 #' 前缀。 - Taylor Wood
按照编写的方式,您首先要查看私有路由,如果用户未经身份验证,则会引发异常。您永远不会成功地为未经身份验证的用户提供公共页面。 - amalloy
1
@amalloy 我在一个Compojure应用程序中使用相同的设置,对于授权路由和其他情况都可以正常工作。但是,我正在使用与OP不同的中间件函数,该函数返回{:status 401}而不是抛出异常。 - Taylor Wood
我不确定为什么,但第一个示例即使调用了“throw-unauthorized”也能正常工作。我只是猜测可能与一些Ring中间件有关,该中间件捕获异常,其中“::type”键可以映射到HTTP状态。 - Kris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接