情况:
我正在使用Gorilla的mux作为路由器构建REST API.
我想知道如何使用简单的HTTP Basic Auth保护特定路由.我不需要从文件或任何外部源读取凭据,我真的只想通过硬编码的HTTP Basic Auth用户名和密码来保护选定的路由.
题:
在Go中这样做的惯用方法是什么?Gorilla是否提供任何东西以使其更容易?如果你能提供几行代码,那就太棒了.
Tim*_*mmm 35
将几个答案组合成一个简单的复制/粘贴:
// BasicAuth wraps a handler requiring HTTP basic auth for it using the given
// username and password and the specified realm, which shouldn't contain quotes.
//
// Most web browser display a dialog with something like:
//
// The website says: "<realm>"
//
// Which is really stupid so you may want to set the realm to a message rather than
// an actual realm.
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)
}
}
...
http.HandleFunc("/", BasicAuth(handleIndex, "admin", "123456", "Please enter your username and password for this site"))
Run Code Online (Sandbox Code Playgroud)
请注意,subtle.ConstantTimeCompare()
仍取决于长度,因此如果您这样做,攻击者可能会计算出用户名和密码的长度.要绕过它,你可以哈希它们或添加一个固定的延迟.
vou*_*rus 23
检查req.BasicAuth() https://golang.org/pkg/net/http/#Request.BasicAuth
你可以在你的处理程序中检查这个,或像这样包装你的处理程序
func auth(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user, pass, _ := r.BasicAuth()
if !check(user, pass) {
http.Error(w, "Unauthorized.", 401)
return
}
fn(w, r)
}
}
Run Code Online (Sandbox Code Playgroud)
哪里
check(u, p string) bool
Run Code Online (Sandbox Code Playgroud)
是您必须根据存储凭据的方式自行编写的函数.现在你可以使用:
auth(originalHandler)
Run Code Online (Sandbox Code Playgroud)
无论你以前在哪里通过originalHandler.
[编辑:值得补充的是,你的检查功能应该能够抵抗定时攻击等侧通道攻击.存储的密码也应使用加密随机盐进行哈希处理.此外,您应该使用OAuth,让已建立的身份提供商担心您的密码安全.]
nem*_*emo 19
截至2016年,我建议使用此答案.在任何情况下,请将您的HTTP基本身份验证包装在SSL中,以避免将用户名和密码作为纯文本发送.
只需将处理程序包装在另一个处理程序中,并在传入请求中使用问题WWW-Authorization标头.
示例(完整版):
func checkAuth(w http.ResponseWriter, r *http.Request) bool {
s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
if len(s) != 2 { return false }
b, err := base64.StdEncoding.DecodeString(s[1])
if err != nil { return false }
pair := strings.SplitN(string(b), ":", 2)
if len(pair) != 2 { return false }
return pair[0] == "user" && pair[1] == "pass"
}
yourRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if checkAuth(w, r) {
yourOriginalHandler.ServeHTTP(w, r)
return
}
w.Header().Set("WWW-Authenticate", `Basic realm="MY REALM"`)
w.WriteHeader(401)
w.Write([]byte("401 Unauthorized\n"))
})
Run Code Online (Sandbox Code Playgroud)
不幸的是,标准.库只提供客户端基本身份验证,因此您必须自己动手或使用库,例如此库.
请求net/http
类型具有用于执行此操作的辅助函数(在 go 1.7 上测试)。尼莫的答案的一个简单版本如下所示:
func basicAuthHandler(user, pass, realm string, next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if checkBasicAuth(r, user, pass) {
next(w, r)
return
}
w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm="%s"`, realm))
w.WriteHeader(401)
w.Write([]byte("401 Unauthorized\n"))
}
}
func checkBasicAuth(r *http.Request, user, pass string) bool {
u, p, ok := r.BasicAuth()
if !ok {
return false
}
return u == user && p == pass
}
Run Code Online (Sandbox Code Playgroud)
然后只需使用业务逻辑创建处理程序并将其作为next
参数传递basicAuthHandler
以创建新的“包装”handlerFunc。