Clojure Kit中JWT认证中间件失效,如何按需应用路由中间件?
问题解决方案
一、修复wrap-auth中间件不生效问题
你的/auth/checked无需携带Authorization头即可访问,核心原因是中间件配置存在两个问题:
- 未指定JWT认证方案:默认JWS后端不会自动解析
Bearer格式的Authorization头,需显式配置; - 授权逻辑缺失:
wrap-authorization依赖明确的权限校验规则,否则不会拦截未认证请求。
修改后的wrap-auth实现:
(require '[buddy.auth.backends.jws :as backends] '[buddy.auth.middleware :as auth-middleware] '[buddy.auth.accessrules :as accessrules]) (defn wrap-auth [handler] (let [backend (backends/jws {:secret "topsecret" :token-name "Bearer"})] ; 指定Bearer认证方案 (-> handler (auth-middleware/wrap-authentication backend) ; 添加基础认证校验规则:必须存在身份信息 (accessrules/wrap-accessrules {:rules [{:pattern #"^/auth/checked" :handler (fn [req] (or (:identity req) {:status 401 :body "Unauthorized"}))}]}) (auth-middleware/wrap-authorization backend))))
如果不需要复杂权限规则,也可以直接在handler内判断身份:
(defn checked [req] (if (:identity req) {:status 200 :body "Authenticated"} {:status 401 :body "Unauthorized"}))
二、为部分路由应用中间件的三种方式
基于Clojure Kit默认的Reitit路由库,有三种常用的局部路由中间件应用方式:
1. 单个路由Handler包装
直接为目标路由的Handler包装中间件,适合单个路由场景:
(defn api-routes [_opts] ["/auth/login" {:post auth/login}] ["/auth/checked" {:get (wrap-auth auth/checked)}] ["/auth/open" {:get auth/open}])
2. 路由组批量包装
将需要统一认证的路由归为一组,用reitit.ring/wrap-routes批量包装:
(require '[reitit.ring :as ring]) (defn api-routes [_opts] ["/auth" ["/login" {:post auth/login}] ["/open" {:get auth/open}] ; 批量包装需要认证的路由分支 (ring/wrap-routes ["/checked" {:get auth/checked}] wrap-auth)])
3. 路由元数据指定中间件
在路由配置的:middleware字段中声明中间件,Reitit会自动应用:
(defn api-routes [_opts] ["/auth/login" {:post auth/login}] ["/auth/checked" {:get auth/checked :middleware [wrap-auth]}] ["/auth/open" {:get auth/open}])
三、JWT Bearer认证的Kit极简示例
以下是完整的可运行示例:
1. 依赖配置(deps.edn)
{:deps {buddy/buddy-auth {:mvn/version "3.0.3"} buddy/buddy-sign {:mvn/version "3.4.1"}}}
2. 认证模块实现
(ns myapp.auth (:require [buddy.auth.backends.jws :as backends] [buddy.auth.middleware :as auth-middleware] [buddy.sign.jwt :as jwt] [ring.util.response :as resp])) (def secret "topsecret") (def jwt-backend (backends/jws {:secret secret :token-name "Bearer"})) (defn wrap-auth [handler] (-> handler (auth-middleware/wrap-authentication jwt-backend) (auth-middleware/wrap-authorization jwt-backend))) ; 生成JWT的登录接口 (defn login [req] (let [user (get-in req [:params :username])] (if user (resp/response {:token (jwt/sign {:username user :role :user} secret)}) (resp/unauthorized "Missing username")))) ; 需认证的接口 (defn checked [req] (resp/response {:message "Authenticated" :user (:identity req)})) ; 开放接口 (defn open [req] (resp/response {:message "Open access allowed"}))
3. 路由定义
(ns myapp.routes (:require [myapp.auth :as auth] [reitit.ring :as ring])) (defn api-routes [_opts] ["/auth" ["/login" {:post auth/login}] ["/open" {:get auth/open}] ["/checked" {:get auth/checked :middleware [auth/wrap-auth]}]])
四、权限限制基础用法
如果需要角色级别的权限控制,可使用buddy.auth.accessrules实现:
1. 定义权限规则
(def access-rules [{:pattern #"^/admin" :handler (fn [req] (let [user (:identity req)] (and user (= (:role user) :admin))))} {:pattern #"^/user" :handler (fn [req] (boolean (:identity req)))}])
2. 包装权限中间件
(defn wrap-access-control [handler] (accessrules/wrap-accessrules handler {:rules access-rules :on-error (fn [_req _err] (resp/unauthorized "Forbidden"))}))
3. 应用到路由
(defn api-routes [_opts] ["/auth" ["/login" {:post auth/login}] ["/open" {:get auth/open}] (ring/wrap-routes ["/checked" {:get auth/checked}] auth/wrap-auth) ; 同时应用认证和权限控制 (ring/wrap-routes ["/admin/panel" {:get admin/panel}] (comp auth/wrap-auth wrap-access-control))])
内容的提问来源于stack exchange,提问作者Patrick Bucher
相关产品推荐
相关产品推荐

