In che modo le persone gestiscono l'autenticazione in Go? [chiuso]


187

Per chi sviluppa API RESTful e app front-end JS in Go, come gestite l'autenticazione? Stai usando qualche libreria o tecnica particolare?

Sono sorpreso di trovare così poche discussioni su questo. Tengo a mente risposte come le seguenti e sto cercando di evitare di sviluppare la mia implementazione:

Modulo di autenticazione in ASP.Net

Ognuno sta codificando la propria soluzione, separatamente?


5
L'autenticazione dipende molto dal tipo di applicazione che stai cercando. Non esiste una soluzione unica per tutti. Inoltre, è un problema difficile da risolvere. Questo è probabilmente il motivo per cui non troverai alcuna documentazione conclusiva.
jimt

21
Ehi, grazie per la rapida risposta. Capito, ma la maggior parte delle lingue e dei framework ha messo a punto soluzioni di autenticazione che coprono i requisiti di autenticazione più comuni condivisi dalla maggior parte delle app e hanno un'ampia partecipazione e supporto da parte della comunità. Sono d'accordo che è un problema difficile. Non traggono maggiori benefici dallo sforzo di cooperazione? (Questa non è una lamentela, perché è open source, ma più un'osservazione che stiamo tutti reinventando la ruota. :)
SexxLuthor

13
@jimt Il fatto che sia un problema difficile rende ancora più importante fornire a noi mortali una soluzione cononica che non possiamo sbagliare.
Tymtam,

Sto votando per chiudere questa domanda come fuori tema perché è una domanda del sondaggio.
Flimzy

Risposte:


115

Questa domanda ha un sacco di punti di vista - e ha un badge di domande popolari - quindi so che c'è molto interesse latente su questo argomento e molte persone chiedono esattamente la stessa cosa e non trovano risposte sugli Interwebs.

La maggior parte delle informazioni disponibili si traduce nell'equivalente testuale della cosa ondulata della mano, lasciata come un "esercizio per il lettore". ;)

Tuttavia ho finalmente trovato un esempio concreto, (generosamente) fornito da un membro della mailing list Golang-Nuts:

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

Ciò fornisce uno schema suggerito e un'implementazione sul lato server come base per l'autenticazione personalizzata. Il codice lato client dipende ancora da te.

(Spero che l'autore del post veda questo: Grazie!)

Estratto (e riformattato):


"Vorrei suggerire qualcosa come il seguente design:

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
)
  • Quando un utente accede al tuo sito tramite un POST in TLS, determina se la password è valida.
  • Quindi emetti una chiave di sessione casuale, ad esempio 50 o più caratteri crittografici e roba in un cookie sicuro.
  • Aggiungi quella chiave di sessione alla tabella UserSession.
  • Quindi quando vedi di nuovo quell'utente, premi prima la tabella UserSession per vedere se SessionKey è lì con un LoginTime valido e LastSeenTime e l'utente non vengono eliminati. Puoi progettarlo in modo che un timer cancelli automaticamente le vecchie righe in UserSession. "

8
Tendiamo a preferire un sito autonomo qui a SO, quindi ti dispiacerebbe pubblicare la soluzione anche qui? Nel caso in cui il link cambi in tempo utile (link rot e cos'altro ...) I futuri visitatori potrebbero essere contenti di questo.
topskip

È una domanda giusta, rispettosamente formulata. Grazie. Ho incluso la soluzione; pensi che dovrebbe essere incluso anche il nome dell'autore? (È pubblico, ma mi chiedo l'etichetta di entrambe le opzioni.)
SexxLuthor

Penso che sia buono così com'è. Non pretendi di essere il "proprietario" di questo frammento e non riesco a vedere che l'autore originale di questo frammento richiede che ogni copia abbia bisogno di un'attribuzione. (Solo i miei due centesimi).
topskip

35
Non ci dovrebbe essere un campo "PasswordSalt" nel tuo database, perché dovresti usare bcrypt come algoritmo di hashing, che crea automaticamente un salt e lo include nell'hash restituito. Utilizzare anche una funzione di confronto dei tempi costante.
0xdabbad00,

4
+1 per criptovaluta. Inoltre, le sessioni gorilla con le sue chiavi di "crittografia" e "autenticazione" consentono di archiviare in modo sicuro le informazioni sulla sessione senza utilizzare una tabella DB.
crantok,


14

Utilizzeresti il ​​middleware per eseguire l'autenticazione.

Puoi provare go-http-auth per l' autenticazione di base e digest e gomniauth per OAuth2.

Ma come eseguire l'autenticazione dipende davvero dalla tua app.

L'autenticazione introduce stato / contesto nel tuo http.Handlers e di recente si è discusso di questo.

Soluzioni ben note al problema del contesto sono gorilla / context e il contesto di google descritti qui .

Ho creato una soluzione più generale senza la necessità di uno stato globale in go-on / wrap che può essere utilizzata insieme o senza le altre due e si integra perfettamente con il middleware senza contesto.

wraphttpauth fornisce l'integrazione di go-http-auth con go-on / wrap.


Ci sono così tante cose nuove con i principianti. Mi chiedo che tipo di cose che un principiante dovrebbe iniziare con esso. go-http-autho gomniauthentrambi?
Casper,

Qualcuno qui ha implementato OAuth 1.0 in Golang? Autenticazione basata su ConsumerKey e Secret?
user2888996

Come posso implementare oAuth 1.0? Usi la chiave del consumatore e il segreto? Per favore aiuto. Non sto ottenendo alcuna libreria per lo stesso.
user2888996

9

Rispondendo a questo nel 2018. Suggerisco di usare JWT (JSON Web Token). La risposta che hai segnato risolto ha lo svantaggio, che è il viaggio che ha fatto davanti (utente) e indietro (server / db). Ciò che è peggio se l'utente ha fatto richieste frequenti che necessitano di autorizzazione, si tradurrà in richieste gonfiate da / a server e database. Per risolvere questo problema, utilizzare JWT che memorizza il token nel lato utente che può essere utilizzato dall'utente ogni volta che necessita di accesso / richiesta. Non è necessario un viaggio nel database e nell'elaborazione del server per verificare che la validità del token richieda poco tempo.


6

Un altro pacchetto open source per la gestione dell'autenticazione con i cookie è httpauth .

(scritto da me, a proposito)


2

Onestamente, ci sono molti metodi e tecniche di autenticazione che puoi montare nella tua applicazione e che dipendono dalla logica e dai requisiti aziendali delle applicazioni.
Ad esempio Oauth2, LDAP, autenticazione locale, ecc. La
mia risposta presuppone che tu stia cercando l'autenticazione locale, il che significa che gestisci le identità dell'utente nella tua applicazione. Il server deve esporre una serie di API esterne che consentano agli utenti e agli amministratori di gestire gli account e il modo in cui desiderano identificarsi sul server per ottenere comunicazioni affidabili. finirai per creare una tabella DB contenente le informazioni dell'utente. dove la password è sottoposta a hash per motivi di sicurezza Vedere Come archiviare la password nel database

assumiamo i requisiti delle app per autenticare gli utenti in base a uno dei seguenti metodi:

  • autenticazione di base (nome utente, password):
    questo metodo di autenticazione dipende dalle serie di credenziali dell'utente nell'intestazione dell'autorizzazione codificate in base64 e definite in rfc7617 , fondamentalmente quando l'app riceve l'utente richiede la decodifica dell'autorizzazione e ricodifica la password per confrontarla all'interno del DB hash se corrisponde all'utente autenticato, altrimenti restituisce il codice di stato 401 all'utente.

  • autenticazione basata su certificato:
    questo metodo di autenticazione dipende da un certificato digitale per identificare un utente ed è noto come x509 auth, quindi quando l'app riceve le richieste dell'utente, legge il certificato del client e verifica che corrisponda al certificato radice CA fornito all'APP.

  • token bearer:
    questo metodo di autenticazione dipende da token di accesso di breve durata, il token bearer è una stringa criptica, generalmente generata dal server in risposta a una richiesta di accesso. quindi quando l'app riceve le richieste dell'utente, legge l'autorizzazione e convalida il token per autenticare l'utente.

Tuttavia, consiglierei go-guardian per la libreria di autenticazione che fa attraverso una serie estensibile di metodi di autenticazione noti come strategie. fondamentalmente Go-Guardian non monta route o assume alcun particolare schema di database, il che massimizza la flessibilità e consente alle decisioni di essere prese dallo sviluppatore.

La configurazione di un autenticatore del guardiano è semplice.

Ecco l'esempio completo dei metodi di cui sopra.

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
}

Uso:

  • Ottieni token:
curl  -k https://127.0.0.1:8080/login -u stackoverflow:stackoverflow
token: 90d64460d14870c08c81352a05dedd3465940a7

  • Autenticare con un token:
curl  -k https://127.0.0.1:8080/resource -H "Authorization: Bearer 90d64460d14870c08c81352a05dedd3465940a7"

Resource!!
  • Autenticare con le credenziali dell'utente:
curl  -k https://127.0.0.1:8080/resource -u stackoverflow:stackoverflow

Resource!!
  • Autenticare con un certificato utente:
curl --cert client.pem --key client-key.pem --cacert ca.pem https://127.0.0.1:8080/resource

Resource!!

È possibile abilitare più metodi di autenticazione contemporaneamente. Di solito dovresti usare almeno due metodi


1

Dai un'occhiata a Labstack Echo : avvolge l'autenticazione per le API RESTful e le applicazioni frontend in middleware che puoi utilizzare per proteggere percorsi API specifici.

L'impostazione dell'autenticazione di base, ad esempio, è semplice come la creazione di un nuovo subrouter per 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
}))

Vedi tutte le opzioni di autenticazione middleware di Labstack qui.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.