Wie verwalten Benutzer die Authentifizierung in Go? [geschlossen]


187

Wie verwalten Sie die Authentifizierung für diejenigen, die RESTful-APIs und JS-Front-End-Apps in Go erstellen? Verwenden Sie bestimmte Bibliotheken oder Techniken?

Ich bin überrascht, so wenig Diskussion darüber zu finden. Ich denke an Antworten wie die folgenden und versuche zu vermeiden, meine eigene Implementierung zu entwickeln:

Authentifizierungsformular in ASP.Net

Codiert jeder seine eigene Lösung separat?


5
Die Authentifizierung hängt stark von der Art der Anwendung ab, nach der Sie suchen. Es gibt keine einheitliche Lösung. Darüber hinaus ist es ein schwer zu lösendes Problem. Dies ist wahrscheinlich der Grund, warum Sie keine schlüssige Dokumentation finden.
Jimt

21
Hey, danke für die schnelle Antwort. Verstanden, aber die meisten Sprachen und Frameworks haben Authentifizierungslösungen entwickelt, die die häufigsten Authentifizierungsanforderungen abdecken, die von den meisten Apps gemeinsam genutzt werden, und eine breite Beteiligung und Unterstützung der Community bieten. Ich stimme zu, dass es ein schweres Problem ist. Profitieren diese nicht am meisten von Kooperationsbemühungen? (Dies ist keine Beschwerde, da dies Open Source ist, sondern eher eine Beobachtung, dass wir alle das Rad neu erfinden. :)
SexxLuthor

12
@jimt Die Tatsache, dass es ein schwieriges Problem ist, macht es noch wichtiger, uns Sterblichen eine kononische Lösung zu liefern, die wir nicht falsch machen können.
Tymtam

Ich stimme dafür, diese Frage als nicht zum Thema gehörend zu schließen, da es sich um eine Umfrage handelt.
Flimzy

Antworten:


115

Diese Frage erhält eine Menge Aufrufe - und hat ein Abzeichen für beliebte Fragen -, daher weiß ich, dass es ein großes latentes Interesse an diesem Thema gibt, und viele Leute fragen genau das Gleiche und finden keine Antworten in den Interwebs.

Die meisten verfügbaren Informationen führen zu dem Textäquivalent der Handwellensache, das als "Übung für den Leser" übrig bleibt. ;)

Ich habe jedoch endlich ein konkretes Beispiel gefunden, das (großzügig) von einem Mitglied der Golang-Nuts-Mailingliste zur Verfügung gestellt wurde:

https://groups.google.com/forum/#!msg/golang-nuts/GE7a_5C5kbA/fdSnH41pOPYJ

Dies bietet ein vorgeschlagenes Schema und eine serverseitige Implementierung als Grundlage für die benutzerdefinierte Authentifizierung. Der clientseitige Code liegt immer noch bei Ihnen.

(Ich hoffe, der Autor des Beitrags sieht dies: Danke!)

Auszug (und neu formatiert):


"Ich würde so etwas wie das folgende Design vorschlagen:

create table User (
 ID int primary key identity(1,1),
 Username text,
 FullName text,
 PasswordHash text,
 PasswordSalt text,
 IsDisabled bool
)

create table UserSession (
 SessionKey text primary key,
 UserID int not null, -- Could have a hard "references User"
 LoginTime <time type> not null,
 LastSeenTime <time type> not null
)
  • Wenn sich ein Benutzer über einen POST unter TLS bei Ihrer Site anmeldet, stellen Sie fest, ob das Kennwort gültig ist.
  • Geben Sie dann einen zufälligen Sitzungsschlüssel aus, z. B. 50 oder mehr Crypto-Rand-Zeichen und Inhalte in einem sicheren Cookie.
  • Fügen Sie diesen Sitzungsschlüssel zur UserSession-Tabelle hinzu.
  • Wenn Sie diesen Benutzer dann wieder sehen, klicken Sie zuerst auf die UserSession-Tabelle, um festzustellen, ob der SessionKey mit einer gültigen LoginTime und LastSeenTime vorhanden ist und der Benutzer nicht gelöscht wird. Sie können es so gestalten, dass ein Timer alte Zeilen in UserSession automatisch löscht. "

8
Wir mögen eine eigenständige Site hier bei SO. Würde es Ihnen also etwas ausmachen, die Lösung auch hier zu veröffentlichen? Nur für den Fall, dass sich der Link rechtzeitig ändert (Link verrotten und was noch ...). Zukünftige Besucher könnten sich darüber freuen.
Topskip

Das ist eine faire Frage, respektvoll ausgedrückt. Danke dir. Ich habe die Lösung aufgenommen. Denken Sie, dass der Name des Autors auch enthalten sein sollte? (Es ist öffentlich, aber ich wundere mich über die Etikette beider Optionen.)
SexxLuthor

Ich denke es ist gut so wie es ist. Sie behaupten nicht, der "Eigentümer" dieses Snippets zu sein, und ich kann nicht sehen, dass der ursprüngliche Autor dieses Snippets verlangt, dass jede Kopie eine Namensnennung benötigt. (Nur meine zwei Cent).
Topskip

35
Es sollte kein "PasswordSalt" -Feld in Ihrer Datenbank geben, da Sie bcrypt als Hashing-Algorithmus verwenden sollten, der automatisch ein Salt erstellt und es in den zurückgegebenen Hash einschließt. Verwenden Sie auch eine konstante Zeitvergleichsfunktion.
0xdabbad00

4
+1 für bcrypt. Außerdem können Sie in Gorilla-Sitzungen mit den Schlüsseln "Verschlüsselung" und "Authentifizierung" Sitzungsinformationen sicher speichern, ohne eine DB-Tabelle zu verwenden.
Crantok


14

Sie würden Middleware verwenden, um die Authentifizierung durchzuführen.

Sie können go-http-auth für Basis- und Digest-Authentifizierung und Gomniauth ausprobieren für OAuth2 ausprobieren.

Wie Sie sich authentifizieren, hängt jedoch von Ihrer App ab.

Durch die Authentifizierung wird der Status / Kontext in Ihre http.Handler eingeführt, und darüber wurde in letzter Zeit diskutiert.

Bekannte Lösungen für das Kontextproblem sind Gorilla / Kontext und Google-Kontext, die hier beschrieben werden .

Ich habe eine allgemeine Lösung , ohne die Notwendigkeit des globalen Zustandes in go-on / Wrap , die zusammen oder ohne die beiden anderen verwendet werden kann und sich gut in kontextfreie Middleware integrieren lässt.

wraphttpauth bietet die Integration von go-http-auth mit go-on / wrap.


Es gibt so viele neue Dinge mit Anfängern. Ich frage mich, was ein Anfänger damit anfangen soll. go-http-authoder gomniauthoder beide?
Casper

Hat hier jemand OAuth 1.0 in Golang implementiert? ConsumerKey- und Secret-basierte Authentifizierung?
user2888996

Wie kann ich oAuth 1.0 implementieren? Consumer Key und Secret verwenden? Bitte helfen Sie. Ich bekomme keine Bibliothek dafür.
user2888996

10

Beantwortung dieser Frage im Jahr 2018. Ich schlage vor, JWT (JSON Web Token) zu verwenden. Die Antwort, die Sie als gelöst markiert haben, hat den Nachteil, dass es sich um die Reise handelt, die sie vorne (Benutzer) und hinten (Server / Datenbank) unternommen hat. Was schlimmer ist, wenn Benutzer häufig Anfragen gestellt haben, die eine Authentifizierung erfordern, führt zu einer aufgeblähten Anfrage von / zu Server und Datenbank. Um dies zu lösen, verwenden Sie JWT, das das Token auf Benutzerseite speichert, das vom Benutzer jederzeit verwendet werden kann, wenn er Zugriff / Anforderung benötigt. Es ist nicht erforderlich, zur Datenbank- und Serververarbeitung zu wechseln, um die Gültigkeit des Tokens zu überprüfen.



2

Ehrlich gesagt gibt es viele Authentifizierungsmethoden und -techniken, die Sie in Ihre Anwendung einbinden können und die von der Geschäftslogik und den Anforderungen der Anwendungen abhängen.
Zum Beispiel Oauth2, LDAP, lokale Authentifizierung usw.
Meine Antwort geht davon aus, dass Sie nach lokaler Authentifizierung suchen, was bedeutet, dass Sie die Benutzeridentitäten in Ihrer Anwendung verwalten. Der Server muss eine Reihe externer APIs bereitstellen, mit denen Benutzer und Administratoren die Konten verwalten können und wie sie sich gegenüber dem Server identifizieren möchten, um eine vertrauenswürdige Kommunikation zu erreichen. Am Ende erstellen Sie eine DB-Tabelle mit den Benutzerinformationen. wo das Passwort aus Sicherheitsgründen gehasht wird Siehe Speichern des Kennworts in der Datenbank

Nehmen wir App-Anforderungen an, um Benutzer anhand einer der folgenden Methoden zu authentifizieren:

  • Basisauthentifizierung (Benutzername, Kennwort):
    Diese Authentifizierungsmethode hängt von den Benutzeranmeldeinformationen ab, die im Autorisierungsheader festgelegt sind, der in base64 codiert und in rfc7617 definiert ist ist. Wenn die App den Benutzer empfängt, fordert er die Dekodierung der Autorisierung an und das Kennwort erneut, um es innerhalb der Datenbank zu vergleichen Hash, wenn es übereinstimmt, hat der authentifizierte Benutzer andernfalls den 401-Statuscode an den Benutzer zurückgegeben.

  • Zertifikatsbasierte Authentifizierung:
    Diese Authentifizierungsmethode hängt von einem digitalen Zertifikat ab, um einen Benutzer zu identifizieren. Sie wird als x509-Authentifizierung bezeichnet. Wenn die App die Benutzeranforderungen empfängt, liest sie das Zertifikat des Clients und überprüft, ob es mit dem bereitgestellten CA-Stammzertifikat übereinstimmt zur APP.

  • Bearer-Token:
    Diese Authentifizierungsmethode hängt von kurzlebigen Zugriffstoken ab. Das Bearer-Token ist eine kryptische Zeichenfolge, die normalerweise vom Server als Antwort auf eine Anmeldeanforderung generiert wird. Wenn die App die Benutzeranforderungen empfängt, liest sie die Autorisierung und validiert das Token, um den Benutzer zu authentifizieren.

Ich würde jedoch Go-Guardian empfehlen für die Authentifizierungsbibliothek die über eine erweiterbare Reihe von Authentifizierungsmethoden ausgeführt wird, die als Strategien bezeichnet werden. Grundsätzlich stellt Go-Guardian keine Routen bereit oder nimmt kein bestimmtes Datenbankschema an, was die Flexibilität maximiert und es dem Entwickler ermöglicht, Entscheidungen zu treffen.

Das Einrichten eines Go-Guardian-Authentifikators ist unkompliziert.

Hier das vollständige Beispiel der oben genannten Methoden.

package main

import (
    "context"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "sync"

    "github.com/golang/groupcache/lru"
    "github.com/gorilla/mux"
    "github.com/shaj13/go-guardian/auth"
    "github.com/shaj13/go-guardian/auth/strategies/basic"
    "github.com/shaj13/go-guardian/auth/strategies/bearer"
    gx509 "github.com/shaj13/go-guardian/auth/strategies/x509"
    "github.com/shaj13/go-guardian/store"
)

var authenticator auth.Authenticator
var cache store.Cache

func middleware(next http.Handler) http.HandlerFunc {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Println("Executing Auth Middleware")
        user, err := authenticator.Authenticate(r)
        if err != nil {
            code := http.StatusUnauthorized
            http.Error(w, http.StatusText(code), code)
            return
        }
        log.Printf("User %s Authenticated\n", user.UserName())
        next.ServeHTTP(w, r)
    })
}

func Resource(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Resource!!\n"))
}

func Login(w http.ResponseWriter, r *http.Request) {
    token := "90d64460d14870c08c81352a05dedd3465940a7"
    user := auth.NewDefaultUser("admin", "1", nil, nil)
    cache.Store(token, user, r)
    body := fmt.Sprintf("token: %s \n", token)
    w.Write([]byte(body))
}

func main() {
    opts := x509.VerifyOptions{}
    opts.KeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
    opts.Roots = x509.NewCertPool()
    // Read Root Ca Certificate
    opts.Roots.AddCert(readCertificate("<root-ca>"))

    cache = &store.LRU{
        lru.New(100),
        &sync.Mutex{},
    }

    // create strategies
    x509Strategy := gx509.New(opts)
    basicStrategy := basic.New(validateUser, cache)
    tokenStrategy := bearer.New(bearer.NoOpAuthenticate, cache)

    authenticator = auth.New()
    authenticator.EnableStrategy(gx509.StrategyKey, x509Strategy)
    authenticator.EnableStrategy(basic.StrategyKey, basicStrategy)
    authenticator.EnableStrategy(bearer.CachedStrategyKey, tokenStrategy)

    r := mux.NewRouter()
    r.HandleFunc("/resource", middleware(http.HandlerFunc(Resource)))
    r.HandleFunc("/login", middleware(http.HandlerFunc(Login)))

    log.Fatal(http.ListenAndServeTLS(":8080", "<server-cert>", "<server-key>", r))
}

func validateUser(ctx context.Context, r *http.Request, userName, password string) (auth.Info, error) {
    // here connect to db or any other service to fetch user and validate it.
    if userName == "stackoverflow" && password == "stackoverflow" {
        return auth.NewDefaultUser("stackoverflow", "10", nil, nil), nil
    }

    return nil, fmt.Errorf("Invalid credentials")
}

func readCertificate(file string) *x509.Certificate {
    data, err := ioutil.ReadFile(file)

    if err != nil {
        log.Fatalf("error reading %s: %v", file, err)
    }

    p, _ := pem.Decode(data)
    cert, err := x509.ParseCertificate(p.Bytes)
    if err != nil {
        log.Fatalf("error parseing certificate %s: %v", file, err)
    }

    return cert
}

Verwendung:

  • Token erhalten:
curl  -k https://127.0.0.1:8080/login -u stackoverflow:stackoverflow
token: 90d64460d14870c08c81352a05dedd3465940a7
  • Mit einem Token authentifizieren:
curl  -k https://127.0.0.1:8080/resource -H "Authorization: Bearer 90d64460d14870c08c81352a05dedd3465940a7"

Resource!!
  • Authentifizieren Sie sich mit einem Benutzerausweis:
curl  -k https://127.0.0.1:8080/resource -u stackoverflow:stackoverflow

Resource!!
  • Mit einem Benutzerzertifikat authentifizieren:
curl --cert client.pem --key client-key.pem --cacert ca.pem https://127.0.0.1:8080/resource

Resource!!

Sie können mehrere Authentifizierungsmethoden gleichzeitig aktivieren. Normalerweise sollten Sie mindestens zwei Methoden anwenden


1

Werfen Sie einen Blick auf Labstack Echo - es packt die Authentifizierung für RESTful-APIs und Frontend-Anwendungen in Middleware, mit der Sie bestimmte API-Routen schützen können.

Das Einrichten der Basisauthentifizierung ist beispielsweise so einfach wie das Erstellen eines neuen Subrouters für die /adminRoute:

e.Group("/admin").Use(middleware.BasicAuth(func(username, password string, c echo.Context) (bool, error) {
    if username == "joe" && password == "secret" {
        return true, nil
    }
    return false, nil
}))

Hier finden Sie alle Middleware-Authentifizierungsoptionen von Labstack.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.