Clojure中基于元信息的声明式数据库回调安全实现方案咨询
优化声明式数据库回调的实现方案
方案一:扩展元数据,限定命名空间范围
给:on-insert元信息增加命名空间校验规则,让它同时包含集合名和允许的命名空间列表(或正则):
(defn ^{:on-insert {:coll :coll :allowed-ns #{"my.app.callbacks" "my.app.extensions"}}} on-doc-insert [ts doc] (do-something-with-doc doc))
启动时的收集逻辑会先检查函数所在命名空间是否在允许列表内,不在就直接跳过。如果需要更灵活的匹配,还可以用正则表达式,比如:allowed-ns #"^my\.app\..*"来匹配所有my.app开头的命名空间。
方案二:用命名空间级元数据批量管控
如果一组回调都集中在某个命名空间里,直接给整个命名空间加标记,收集逻辑只处理带标记的命名空间下的回调函数:
(ns my.app.callbacks {:db-callback-namespace true}) ; 给命名空间打标记 (defn ^{:on-insert :coll} on-doc-insert [ts doc] (do-something-with-doc doc)) (defn ^{:on-insert :users} on-user-insert [ts doc] (do-something-with-user doc))
这种方式不用给每个函数单独加命名空间规则,只需在收集时过滤出带有:db-callback-namespace true的命名空间即可。
方案三:自定义宏封装声明与校验
写一个专用宏替代defn,在宏内部直接完成命名空间校验和回调注册,既保持声明式的简洁,又在编译阶段就拦截非法命名空间:
(defmacro def-db-callback [name coll & body] (let [current-ns (str *ns*) allowed-ns #{"my.app.callbacks"}] (when-not (contains? allowed-ns current-ns) (throw (ex-info (str "命名空间 " current-ns " 无权定义数据库回调") {}))) `(do (defn ~name [ts# doc#] ~@body) (db/on-insert ~coll ~name))))
使用时直接调用宏,无需额外注册步骤:
(def-db-callback on-doc-insert :coll (do-something-with-doc doc))
方案四:结合组件系统做可控注册
如果项目用了mount或integrant这类组件框架,可以把回调注册绑定到组件生命周期,通过配置指定允许的命名空间:
(defn init-db-callbacks [config] (let [allowed-ns (get config :db-callback-namespaces ["my.app.callbacks"])] (doseq [ns-name allowed-ns :let [ns (find-ns (symbol ns-name)) vars (ns-interns ns)]] (doseq [[var-name var] vars :let [coll (:on-insert (meta var))]] (when coll (db/on-insert coll (var-get var)))))))
通过配置文件灵活调整允许的命名空间,只有组件初始化时才会执行注册,避免全局无差别收集的安全隐患。
内容的提问来源于stack exchange,提问作者Lars Melander
相关产品推荐
相关产品推荐

