【发布时间】:2017-07-08 09:18:52
【问题描述】:
我正在尝试使用gorilla/mux 创建路由,其中一些应受基本身份验证保护,而另一些则不应。具体来说,/v2 下的每条路由都应该需要基本身份验证,但/health 下的路由应该是可公开访问的。
正如您在下面看到的,我可以用BasicAuth() 包装我的每个/v2 路由处理程序,但这违反了DRY 原则,而且容易出错,更不用说忘记包装其中一个的安全隐患了处理程序。
我有来自curl 的以下输出。除了最后一个之外,一切都如我所料。未经身份验证,一个人应该无法GET /smallcat。
$ curl localhost:3000/health/ping
"PONG"
$ curl localhost:3000/health/ping/
404 page not found
$ curl localhost:3000/v2/bigcat
Unauthorised.
$ curl apiuser:apipass@localhost:3000/v2/bigcat
"Big MEOW"
$ curl localhost:3000/v2/smallcat
"Small Meow"
这是完整的代码。我相信我需要以某种方式修复 v2Router 定义,但看不到如何解决。
package main
import (
"crypto/subtle"
"encoding/json"
"log"
"net/http"
"github.com/gorilla/mux"
)
func endAPICall(w http.ResponseWriter, httpStatus int, anyStruct interface{}) {
result, err := json.MarshalIndent(anyStruct, "", " ")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(httpStatus)
w.Write(result)
}
func BasicAuth(handler http.HandlerFunc, username, password, realm string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user, pass, ok := r.BasicAuth()
if !ok || subtle.ConstantTimeCompare([]byte(user), []byte(username)) != 1 || subtle.ConstantTimeCompare([]byte(pass), []byte(password)) != 1 {
w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`)
w.WriteHeader(401)
w.Write([]byte("Unauthorised.\n"))
return
}
handler(w, r)
}
}
func routers() *mux.Router {
username := "apiuser"
password := "apipass"
noopHandler := func(http.ResponseWriter, *http.Request) {}
topRouter := mux.NewRouter().StrictSlash(false)
healthRouter := topRouter.PathPrefix("/health/").Subrouter()
v2Router := topRouter.PathPrefix("/v2").HandlerFunc(BasicAuth(noopHandler, username, password, "Provide username and password")).Subrouter()
healthRouter.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
endAPICall(w, 200, "PONG")
})
v2Router.HandleFunc("/smallcat", func(w http.ResponseWriter, r *http.Request) {
endAPICall(w, 200, "Small Meow")
})
bigMeowFn := func(w http.ResponseWriter, r *http.Request) {
endAPICall(w, 200, "Big MEOW")
}
v2Router.HandleFunc("/bigcat", BasicAuth(bigMeowFn, username, password, "Provide username and password"))
return topRouter
}
func main() {
if r := routers(); r != nil {
log.Fatal("Server exited:", http.ListenAndServe(":3000", r))
}
}
【问题讨论】:
-
应用程序仅包装 /v2 处理程序。包装每个处理程序。
-
goji/httpauth包声称只用一个围绕根处理程序的包装器来保护所有路由。在github.com/goji/httpauth#gorillamux 上查看他们的示例我也尝试使用该软件包来保护/v2,但没有任何成功。 -
也许你需要像中间件这样的东西来完成这种工作:github.com/urfave/negroni
-
goji/httpauth 示例包装了 Gorilla 多路复用器,因此保护了所有路径。问题中的代码包装了一个处理程序(并且该处理程序什么都不做)。
-
如果您指的是
noopHandler,那是故意的。如果未提供用户名:密码对,我希望调用BasicAuth()以停止处理/v2下的其余路由。
标签: go basic-authentication router gorilla mux