Comment les utilisateurs gèrent-ils l'authentification dans Go? [fermé]

191

Pour ceux qui créent des API RESTful et des applications frontales JS dans Go, comment gérez-vous l'authentification? Utilisez-vous des bibliothèques ou des techniques particulières?

Je suis surpris de trouver si peu de discussions à ce sujet. Je garde à l'esprit des réponses comme celles-ci et j'essaie d'éviter de développer ma propre implémentation:

Formulaire d'authentification dans ASP.Net

Tout le monde codifie-t-il sa propre solution, séparément?

SexxLuthor
la source
6
L'authentification dépend beaucoup du type d'application que vous recherchez. Il n’existe pas de solution universelle. De plus, c'est un problème difficile à résoudre. C'est probablement pourquoi vous ne trouverez aucune documentation concluante.
jimt
22
Hé, merci pour la réponse rapide. Compris, mais la plupart des langages et des frameworks ont mis au point des solutions d'authentification qui couvrent les exigences d'authentification les plus courantes partagées par la majorité des applications, et bénéficient d'une large participation et assistance de la communauté. Je reconnais que c'est un problème difficile. Ceux-ci ne bénéficient-ils pas le plus des efforts de coopération? (Ce n'est pas une plainte, car c'est open source, mais plutôt une observation que nous réinventons tous la roue. :)
SexxLuthor
14
@jimt Le fait que ce soit un problème difficile rend encore plus important de nous fournir, les mortels, une solution cononique que nous ne pouvons pas nous tromper.
tymtam
Je vote pour clore cette question comme hors sujet car il s'agit d'une question de sondage.
Flimzy

Réponses:

115

Cette question reçoit une tonne de points de vue - et a un badge de question populaire - donc je sais qu'il y a beaucoup d'intérêt latent pour ce sujet, et beaucoup de gens demandent exactement la même chose et ne trouvent pas de réponses sur les Interwebs.

La plupart des informations disponibles aboutissent à l'équivalent textuel de la chose ondulée à la main, laissée comme «exercice pour le lecteur». ;)

Cependant, j'ai enfin trouvé un exemple concret, (généreusement) fourni par un membre de la liste de diffusion golang-nut:

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

Cela fournit un schéma suggéré et une implémentation côté serveur comme base pour l'authentification personnalisée. Le code côté client dépend toujours de vous.

(J'espère que l'auteur du message voit ceci: Merci!)

Extrait (et reformaté):


"Je suggérerais quelque chose comme la conception suivante:

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
)
  • Lorsqu'un utilisateur se connecte à votre site via un POST sous TLS, déterminez si le mot de passe est valide.
  • Ensuite, émettez une clé de session aléatoire, disons 50 caractères cryptographiques ou plus et des éléments dans un cookie sécurisé.
  • Ajoutez cette clé de session à la table UserSession.
  • Ensuite, lorsque vous voyez à nouveau cet utilisateur, appuyez d'abord sur la table UserSession pour voir si la SessionKey y est avec un LoginTime valide et LastSeenTime et l'utilisateur n'est pas supprimé. Vous pouvez le concevoir de manière à ce qu'un minuteur efface automatiquement les anciennes lignes dans UserSession. "
SexxLuthor
la source
8
Nous avons tendance à aimer un site autonome ici chez SO, alors cela vous dérangerait-il de publier la solution ici également? Juste au cas où le lien changerait en temps voulu (pourriture du lien et quoi d'autre ...) Les futurs visiteurs pourraient s'en réjouir.
topskip
C'est une question juste, respectueusement posée. Merci. J'ai inclus la solution; pensez-vous que le nom de l'auteur devrait également être inclus? (C'est public, mais je m'interroge sur l'étiquette de l'une ou l'autre option.)
SexxLuthor
Je pense que c'est bon comme ça. Vous ne prétendez pas être le "propriétaire" de cet extrait, et je ne vois pas que l'auteur original de cet extrait exige que chaque copie nécessite une attribution. (Seulement mes deux cents).
topskip
35
Il ne devrait pas y avoir de champ "PasswordSalt" dans votre base de données, car vous devez utiliser bcrypt comme algorithme de hachage, qui crée automatiquement un salt et l'inclut dans le hachage renvoyé. Utilisez également une fonction de comparaison à temps constant.
0xdabbad00
4
+1 pour bcrypt. De plus, les sessions gorilla avec ses clés de «cryptage» et «d'authentification» vous permettraient de stocker en toute sécurité les informations de session sans utiliser de table de base de données.
crantok le
14

Vous utiliseriez un middleware pour effectuer l'authentification.

Vous pouvez essayer go-http-auth pour l'authentification de base et digest et gomniauth pour OAuth2.

Mais comment s'authentifier dépend vraiment de votre application.

L'authentification introduit l'état / le contexte dans vos http.Handlers et il y a eu des discussions à ce sujet récemment.

Les solutions bien connues au problème de contexte sont gorilla / context et google context décrits ici .

J'ai fait une solution plus générale sans avoir besoin d'un état global dans go-on / wrap qui peut être utilisé ensemble ou sans les deux autres et s'intègre bien avec un middleware sans contexte.

wraphttpauth fournit l'intégration de go-http-auth avec go-on / wrap.

metakeule
la source
Il y a tellement de nouveautés avec les débutants. Je me demande avec quel genre de chose un débutant devrait commencer. go-http-authou gomniauthou les deux?
Casper
Quelqu'un ici a implémenté OAuth 1.0 dans Golang? Authentification basée sur ConsumerKey et Secret?
user2888996
Comment puis-je implémenter oAuth 1.0? Utilisation de la clé et du secret du consommateur? Veuillez aider. Je n'obtiens aucune bibliothèque pour la même chose.
user2888996
9

Répondre à cela en 2018. Je suggère d'utiliser JWT (JSON Web Token). La réponse que vous avez marquée comme résolue présente un inconvénient, qui est le trajet qu'elle a effectué avant (utilisateur) et arrière (serveur / db). Ce qui est pire si l'utilisateur a fait une demande fréquente nécessitant une authentification, se traduira par une demande gonflée de / vers le serveur et la base de données. Pour résoudre ce problème, utilisez JWT qui stocke le jeton dans la partie utilisateur qui peut être utilisé par l'utilisateur à tout moment où il a besoin d'un accès / d'une demande. Pas besoin de se rendre dans la base de données et le traitement du serveur pour vérifier la validité du jeton prend peu de temps.

mfathirirhas
la source
6

Un autre package open source pour gérer l'authentification avec les cookies est httpauth .

(écrit par moi, au fait)

Cameron Little
la source
4

Honnêtement, il existe de nombreuses méthodes et techniques d'authentification que vous pouvez monter dans votre application et qui dépendent de la logique et des exigences métier des applications.
Par exemple Oauth2, LDAP, authentification locale, etc.
Ma réponse suppose que vous recherchez une authentification locale, ce qui signifie que vous gérez les identités de l'utilisateur dans votre application. Le serveur doit exposer un ensemble d'API externes permettant aux utilisateurs et aux administrateurs de gérer les comptes et la manière dont ils souhaitent s'identifier auprès du serveur pour établir une communication fiable. vous finirez par créer une table DB contenant les informations de l'utilisateur. où le mot de passe est haché à des fins de sécurité Voir Comment stocker le mot de passe dans la base de données

laissez supposer les exigences de l'application pour authentifier les utilisateurs en fonction de l'une des méthodes suivantes:

  • authentification de base (nom d'utilisateur, mot de passe):
    cette méthode d'authentification dépend des ensembles d'informations d'identification de l'utilisateur dans l'en-tête d'autorisation codé en base64 et défini dans rfc7617 , essentiellement lorsque l'application reçoit, l'utilisateur demande son décode l'autorisation et le hachage du mot de passe pour le comparer dans DB hachage s'il correspond à l'utilisateur authentifié, sinon renvoie le code d'état 401 à l'utilisateur.

  • authentification basée
    sur un certificat : cette méthode d'authentification dépend d'un certificat numérique pour identifier un utilisateur, et elle est connue sous le nom d'authentification x509.Ainsi, lorsque l'application reçoit les demandes de l'utilisateur, elle lit le certificat du client et vérifie qu'il correspond au certificat racine de l'autorité de certification fourni à l'APP.

  • jeton de support:
    cette méthode d'authentification dépend de jetons d'accès de courte durée. Le jeton de support est une chaîne cryptique, généralement générée par le serveur en réponse à une demande de connexion. Ainsi, lorsque l'application reçoit les demandes de l'utilisateur, elle lit l'autorisation et valide le jeton pour authentifier l'utilisateur.

Cependant, je recommanderais go-guardian pour la bibliothèque d'authentification, ce qu'il fait via un ensemble extensible de méthodes d'authentification appelées stratégies. Fondamentalement, Go-Guardian ne monte pas de routes ou n'assume aucun schéma de base de données particulier, ce qui maximise la flexibilité et permet au développeur de prendre des décisions.

La configuration d'un authentificateur go-guardian est simple.

Voici l'exemple complet des méthodes ci-dessus.

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
}

Usage:

  • Obtenez un jeton:
curl  -k https://127.0.0.1:8080/login -u stackoverflow:stackoverflow
token: 90d64460d14870c08c81352a05dedd3465940a7

  • Authentifiez-vous avec un jeton:
curl  -k https://127.0.0.1:8080/resource -H "Authorization: Bearer 90d64460d14870c08c81352a05dedd3465940a7"

Resource!!
  • Authentifiez-vous avec un identifiant utilisateur:
curl  -k https://127.0.0.1:8080/resource -u stackoverflow:stackoverflow

Resource!!
  • Authentifiez-vous avec un certificat utilisateur:
curl --cert client.pem --key client-key.pem --cacert ca.pem https://127.0.0.1:8080/resource

Resource!!

Vous pouvez activer plusieurs méthodes d'authentification à la fois. Vous devez généralement utiliser au moins deux méthodes

shaj13
la source
1

Jetez un œil à Labstack Echo - il encapsule l' authentification pour les API RESTful et les applications frontales dans un middleware que vous pouvez utiliser pour protéger des routes d'API spécifiques.

La configuration de l'authentification de base, par exemple, est aussi simple que la création d'un nouveau sous-routeur pour la /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
}))

Voir toutes les options d'authentification middleware de Labstack ici.

Adil B
la source