在Go中需要HTTP Basic Auth的惯用法?

时间:2014-02-21 13:52:48

标签: go

情况:

我正在使用Gorilla的mux作为路由器构建REST API。

我想知道如何使用简单的HTTP Basic Auth保护特定路由。我不需要从文件或任何外部源读取凭据,我真的只想通过硬编码的HTTP Basic Auth用户名和密码来保护选定的路由。

问题:

在Go中这样做的惯用方法是什么? Gorilla是否提供任何东西以使其更容易?如果你能提供几行代码,那就太棒了。

6 个答案:

答案 0 :(得分:32)

将一些答案组合成一个简单的复制/粘贴:

foreach(var foo in foos)
{
htmlOutput += $"<input asp-for='{foo.FieldName}' />";
}

请注意,// 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")) 仍取决于长度,因此如果您这样做,攻击者可能会计算出用户名和密码的长度。为了解决这个问题,你可以对它们进行哈希处理或添加一个固定的延迟。

答案 1 :(得分:22)

检查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)
  }
}

哪里

check(u, p string) bool 

是您必须根据存储凭据的方式自行编写的功能。现在你可以使用:

auth(originalHandler)

之前你在哪里传递originalHandler。

[编辑:值得补充的是,您的检查功能应该能够抵抗时间攻击等侧通道攻击。存储的密码也应使用加密随机盐进行哈希处理。此外,您应该使用OAuth,让已建立的身份提供商担心密码安全。]

答案 2 :(得分:18)

截至2016年,我建议使用this answer。在任何情况下,请将您的HTTP基本身份验证包装在SSL中,以避免以纯文本形式发送用户名和密码。


将处理程序包装在另一个处理程序中,并在传入请求中使用问题WWW-Authorization标头。

示例(full version):

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"))
})
不幸的是,std。图书馆只提供client basic auth,因此您必须这样做 自己动手或使用库,例如​​this one

答案 3 :(得分:3)

net/http请求类型具有执行此操作的辅助函数(在1.7版中测试)。一个简单版本的nemo的答案看起来像这样:

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
}

然后简单地创建具有业务逻辑的处理程序,并将其作为next中的basicAuthHandler参数传递,以创建一个新的&#34;包装&#34; handlerFunc。

答案 4 :(得分:2)

go-http-auth会为你做的。如果您使用net/http,它将适合您。

答案 5 :(得分:1)

我意识到我在这里参加派对已经很晚了。我刚碰巧正在重新访问HTTP基本身份验证。经过这里的所有答案后,我实际上确定了Timmmm解决方案的变体。我甚至更进了一步,根据他的建议添加了散列以提高安全性。想想我也可以分享我的代码变体。

balance_data = read.table(file.choose(), sep=",")
attach(balance_data)
x <- balance_data[, c(2,3,4,5)]
y <- balance_data[,1]
X_train <-head(x,500)
Y_train <- head(y,100)
X_test <-tail(x,122)
str(X_train)
str(X_test)
str(Y_train)


decisionTree_Learnruntime = c()
svm_Learnruntime = c()
naivebayes_Learnruntime = c()
knn_Learnruntime = c()

decisionTree_Predictruntime = c()
svm_Predictruntime = c()
naivebayes_Predictruntime =c()
knn_Predictruntime = c()


for (i in 1:20){
  library(e1071)
  library(caret)
  #SVM Model
  start.time <- Sys.time()
  svm_model <- svm(X_train,Y_train)
  end.time <- Sys.time()
  time.taken <- end.time - start.time
  svm_Learnruntime[i]<- time.taken

  #Prediction Time
  start.time <- Sys.time()
  pred <- predict(svm_model,X_test)
  end.time <- Sys.time()
  time.taken <- end.time - start.time
  svm_Predictruntime[i]<- time.taken

  library(rpart)
  #Decision Tree
  #Learn Time
  start.time <- Sys.time()
  tree_model <- rpart(X_train,Y_train)
  end.time <- Sys.time()
  time.taken <- end.time - start.time
  decisionTree_Learnruntime[i]<- time.taken

  #Prediction Time 
  start.time <- Sys.time()
  pred = predict(tree_model,X_test)
  end.time <- Sys.time()
  time.taken <- end.time - start.time
  decisionTree_Predictruntime[i] <- time.taken


  library(e1071)
  #Naive Bayes
  #Learn Time
  start.time <- Sys.time()
  naive_model <-naiveBayes(X_train,Y_train)
  end.time <- Sys.time()
  time.taken <- end.time - start.time
  naivebayes_Learnruntime[i]<- time.taken

  #Prediction Time
  start.time <- Sys.time()
  pred = predict(naive_model,X_test)
  end.time <- Sys.time()
  time.taken <- end.time - start.time
  naivebayes_Predictruntime [i]<- time.taken



}

svm_Learnruntime
svm_Predictruntime
decisionTree_Learnruntime
decisionTree_Predictruntime
naivebayes_Learnruntime
naivebayes_Predictruntime