我目前正在写使用的Compojure(和环以及相关的中间件)Clojure中的API。
我试图根据应用的路线不同的认证码。 考虑下面的代码:
(defroutes public-routes
(GET "/public-endpoint" [] ("PUBLIC ENDPOINT")))
(defroutes user-routes
(GET "/user-endpoint1" [] ("USER ENDPOINT 1"))
(GET "/user-endpoint2" [] ("USER ENDPOINT 1")))
(defroutes admin-routes
(GET "/admin-endpoint" [] ("ADMIN ENDPOINT")))
(def app
(handler/api
(routes
public-routes
(-> user-routes
(wrap-basic-authentication user-auth?)))))
(-> admin-routes
(wrap-basic-authentication admin-auth?)))))
如预期,因为这不工作wrap-basic-authentication
,这样会将它不管包裹路线的尝试的确包装路线。 具体而言,如果请求需要路由到admin-routes
, user-auth?
仍然会尝试(和失败)。
我使出context
根在一个共同的基本路径部分航线,但它是一个相当的限制(下面的代码可能无法正常工作,它只是说明的想法):
(defroutes user-routes
(GET "-endpoint1" [] ("USER ENDPOINT 1"))
(GET "-endpoint2" [] ("USER ENDPOINT 1")))
(defroutes admin-routes
(GET "-endpoint" [] ("ADMIN ENDPOINT")))
(def app
(handler/api
(routes
public-routes
(context "/user" []
(-> user-routes
(wrap-basic-authentication user-auth?)))
(context "/admin" []
(-> admin-routes
(wrap-basic-authentication admin-auth?))))))
我想知道如果我失去了一些东西,或者有什么办法都达到了我想不上我的约束defroutes
并没有使用常见的基本路径(如理想情况下,根本不会有人)。
Answer 1:
(defroutes user-routes*
(GET "-endpoint1" [] ("USER ENDPOINT 1"))
(GET "-endpoint2" [] ("USER ENDPOINT 1")))
(def user-routes
(-> #'user-routes*
(wrap-basic-authentication user-auth?)))
(defroutes admin-routes*
(GET "-endpoint" [] ("ADMIN ENDPOINT")))
(def admin-routes
(-> #'admin-routes*
(wrap-basic-authentication admin-auth?)))
(defroutes main-routes
(ANY "*" [] admin-routes)
(ANY "*" [] user-routes)
这将首先通过管理的路由,然后运行传入的请求通过用户路径,在这两种情况下应用正确的验证。 这里的主要思想是,您的身份验证功能,应返回nil
,如果路线不给调用者,而不是抛出一个错误访问。 这样,管理,航线将返回nil,如果A)的路线其实并不符合定义的管理,路由或b)该用户没有必要的身份验证。 如果管理的路由返回nil,用户线路将通过的Compojure受审。
希望这可以帮助。
编辑:我写了一个关于后的Compojure一段时间回来,这可能对您有用: https://vedang.me/techlog/2012-02-23-composability-and-compojure/
Answer 2:
我无意中发现了这个问题,它似乎wrap-routes
(1.3.2的Compojure)解决优雅:
(def app
(handler/api
(routes
public-routes
(-> user-routes
(wrap-routes wrap-basic-authentication user-auth?)))))
(-> admin-routes
(wrap-routes wrap-basic-authentication admin-auth?)))))
Answer 3:
这是一个合理的问题,我惊奇地发现猫腻,当我碰到了它自己。
我想你想要的是这样的:
(defroutes public-routes
(GET "/public-endpoint" [] ("PUBLIC ENDPOINT")))
(defroutes user-routes
(GET "/user-endpoint1" _
(wrap-basic-authentication
user-auth?
(fn [req] (ring.util.response/response "USER ENDPOINT 1"))))
(GET "/user-endpoint2" _
(wrap-basic-authentication
user-auth?
(fn [req] (ring.util.response/response "USER ENDPOINT 1")))))
(defroutes admin-routes
(GET "/admin-endpoint" _
(wrap-basic-authentication
admin-auth? (fn [req] (ring.util.response/response "ADMIN ENDPOINT")))))
(def app
(handler/api
(routes
public-routes
user-routes
admin-routes)))
有两点需要注意:认证中间件是路由表格内,中间件调用一个匿名函数是一个真正的处理程序。 为什么?
正如你所说,你需要经过路由申请认证的中间件,或者请求不会被路由到认证中间件! 换句话说,路由需要被认证环外侧的中间件环上。
如果您使用的Compojure的路由表以获得更多信息,并且你在形式的人体应用中间件,然后中间件功能需要作为其参数真正的环响应处理(即,需要一个请求,并返回一个响应的功能);而不是简单的东西像一个字符串或响应地图。
这是因为,根据定义,像总结基本认证中间件功能只需要处理作为参数,而不是裸露的字符串或响应地图或其他任何东西。
那么,为什么会这样容易错过呢? 其原因是,该路由的Compojure像运营商(GET [路径ARGS和身体] ...)尝试是非常灵活,你被允许在体内场通过什么样的形式让一切变得简单为您服务。 你可以通过在一个真正的处理函数,或者只是一个字符串,或响应地图,或可能是别的东西没有发生在我身上。 在这一切都奠定了render
在内部的Compojure多方法。
这种灵活性掩盖什么GET形式实际上是在做,所以当你尝试做的东西有点不同很容易搞混。
在我看来,与vedang领先的回答这个问题是不是在大多数情况下,一个伟大的想法。 它本质上使用了的意思来回答这个问题的Compojure机械“是否匹配路由的要求?” (如果没有,返回nil)也回答“是否要求通过认证?” 因为通常你要失败的认证与401个状态码返回正确的应对,按照HTTP规范请求,这是有问题的。 在这个问题的答案,可以考虑,如果你增加了失败的管理员认证,以这个例子这样的错误反应将发生有效的用户身份验证的要求是什么:所有有效的用户身份验证的请求将失败,并在管理路由层给出错误。
Answer 4:
我刚刚发现,解决同样的问题如下无关的网页:
http://compojureongae.posterous.com/using-the-app-engine-users-api-from-clojure
我没有意识到它可能使用该类型的语法(这我还没有测试):
(defroutes public-routes
(GET "/public-endpoint" [] ("PUBLIC ENDPOINT")))
(defroutes user-routes
(GET "/user-endpoint1" [] ("USER ENDPOINT 1"))
(GET "/user-endpoint2" [] ("USER ENDPOINT 1")))
(defroutes admin-routes
(GET "/admin-endpoint" [] ("ADMIN ENDPOINT")))
(def app
(handler/api
(routes
public-routes
(ANY "/user*" []
(-> user-routes
(wrap-basic-authentication user-auth?)))
(ANY "/admin*" []
(-> admin-routes
(wrap-basic-authentication admin-auth?))))))
Answer 5:
你有没有考虑使用沙洲 ? 它采用基于角色的授权,并允许您指定声明需要哪些角色访问特定资源。 检查沙洲的文档的详细信息,但它可能工作是这样的(注意引用一个虚构的my-auth-function
,这就是你把你的验证码):
(def security-policy
[#"/admin-endpoint.*" :admin
#"/user-endpoint.*" :user
#"/public-endpoint.*" :any])
(defroutes my-routes
(GET "/public-endpoint" [] ("PUBLIC ENDPOINT"))
(GET "/user-endpoint1" [] ("USER ENDPOINT1"))
(GET "/user-endpoint2" [] ("USER ENDPOINT2"))
(GET "/admin-endpoint" [] ("ADMIN ENDPOINT"))
(def app
(-> my-routes
(with-security security-policy my-auth-function)
wrap-stateful-session
handler/api))
Answer 6:
我会转移你怎么收场处理一般的身份验证裂开认证和路由过滤的过程进行认证。
而不是只具有管理-auth的? 和用户身份验证? 返回布尔值或用户名,用它作为多,你可以在更多的每个路由级别的过滤器上,而不需要“重新认证”的不同路线的“访问级别”键。
(defn auth [user pass]
(cond
(admin-auth? user pass) :admin
(user-auth? user pass) :user
true :unauthenticated))
您还需要考虑一个替代为这条道路将现有的基本认证的中间件。 因为它是目前设计的,它永远返回{:status 401}
如果你不提供凭据,所以你必须考虑到这一点,并让它继续通过代替。
这样做的结果是放在:basic-authentication
请求中的地图,然后你就可以在你想要的水平筛选键。
主要的“过滤”案件脑海中出现:
- 在上下文级别(如你在你的答案是什么),但你可以过滤掉的请求没有必要的
:basic-authentication
密钥 - 每路径水平,在那里你回到它如何认证的本地检查后401响应。 请注意,这是除非你做个别路线的情况下级过滤,你会得到404和401S之间的区别的唯一途径。
- 一个页面取决于身份验证级别的不同看法
要记住的最重要的事情是,你必须要继续,除非被要求需要认证的URL,无效的路由反馈为零。 你需要通过返回401,这将导致环停止尝试其它路由/手柄,以确保你不会过滤掉超过你的想象。
文章来源: Compojure routes with different middleware