Bagaimana orang mengelola otentikasi di Go? [Tutup]


187

Bagi mereka yang membangun RESTful APIs dan aplikasi JS front-end di Go, bagaimana Anda mengelola otentikasi? Apakah Anda menggunakan perpustakaan atau teknik tertentu?

Saya terkejut menemukan begitu sedikit diskusi tentang ini. Saya ingat jawaban seperti berikut, dan saya berusaha menghindari mengembangkan implementasi saya sendiri:

Formulir Otentikasi di ASP.Net

Apakah semua orang mengkode solusi mereka sendiri, secara terpisah?


5
Otentikasi sangat tergantung pada jenis aplikasi yang Anda cari. Tidak ada solusi satu ukuran untuk semua. Selain itu, ini adalah masalah yang sulit untuk dipecahkan. Ini mungkin mengapa Anda tidak akan menemukan dokumentasi konklusif.
Tepat

21
Hei, terima kasih atas tanggapan cepatnya. Dipahami, tetapi sebagian besar bahasa dan kerangka kerja telah hadir dengan solusi otentikasi yang mencakup persyaratan otentikasi paling umum yang dimiliki bersama oleh sebagian besar aplikasi, dan memiliki partisipasi dan dukungan masyarakat luas. Saya setuju bahwa ini masalah yang sulit. Bukankah itu yang paling diuntungkan dari upaya kerja sama? (Ini bukan keluhan, karena ini open source, tetapi lebih dari pengamatan bahwa kita semua menciptakan kembali roda. :)
SexxLuthor

13
@jimt Fakta bahwa ini adalah masalah yang sulit menjadikannya lebih penting untuk memasok kita dengan solusi yang tidak masuk akal bahwa kita tidak dapat salah.
tymtam

Saya memberikan suara untuk menutup pertanyaan ini sebagai di luar topik karena ini adalah pertanyaan polling.
Flimzy

Jawaban:


115

Pertanyaan ini mendapatkan banyak pandangan - dan memiliki lencana Pertanyaan Populer - jadi saya tahu ada banyak minat laten dalam topik ini, dan banyak orang menanyakan hal yang persis sama dan tidak menemukan jawaban pada Jalinan.

Sebagian besar informasi yang tersedia menghasilkan padanan teks dari hal yang bergelombang tangan, dibiarkan sebagai "latihan untuk pembaca." ;)

Namun saya akhirnya menemukan satu contoh nyata, (dengan murah hati) disediakan oleh anggota milis golang-kacang:

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

Ini memberikan skema yang disarankan dan implementasi sisi server sebagai dasar untuk otentikasi kustom. Kode sisi klien masih terserah Anda.

(Saya harap penulis posting melihat ini: Terima kasih!)

Dikutip (dan diformat ulang):


"Saya akan menyarankan sesuatu seperti desain berikut:

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
)
  • Ketika pengguna masuk ke situs Anda melalui POST di bawah TLS, tentukan apakah kata sandi tersebut valid.
  • Kemudian keluarkan kunci sesi acak, ucapkan 50 atau lebih karakter crypto rand dan item dalam Cookie aman.
  • Tambahkan kunci sesi itu ke tabel UserSession.
  • Kemudian ketika Anda melihat pengguna itu lagi, pertama tekan tabel UserSession untuk melihat apakah SessionKey ada di sana dengan LoginTime dan LastSeenTime dan Pengguna yang tidak terhapus. Anda dapat mendesainnya sehingga timer secara otomatis menghapus baris lama di UserSession. "

8
Kami cenderung menyukai situs mandiri di SO, jadi apakah Anda keberatan memposting solusi di sini juga? Untuk berjaga-jaga jika tautannya berubah pada waktunya (tautan busuk dan apa lagi ...) Pengunjung di masa mendatang mungkin senang dengan hal ini.
topskip

Itu pertanyaan yang adil, dengan hormat. Terima kasih. Saya sudah memasukkan solusinya; Menurut Anda apakah nama penulis juga harus dimasukkan? (Ini terbuka untuk umum, tetapi saya ingin tahu tentang etiket dari kedua opsi itu.)
SexxLuthor

Saya pikir itu bagus. Anda tidak mengklaim sebagai "pemilik" cuplikan ini, dan saya tidak dapat melihat bahwa pembuat asli cuplikan ini mengharuskan setiap salinan memerlukan atribusi. (Hanya dua sen saya).
topskip

35
Seharusnya tidak ada bidang "Kata sandi" di database Anda, karena Anda harus menggunakan bcrypt sebagai algoritma hashing Anda, yang secara otomatis membuat garam dan memasukkannya dalam hash yang dikembalikan. Juga gunakan fungsi perbandingan waktu yang konstan.
0xdabbad00

4
+1 untuk bcrypt. Juga, sesi gorila dengan kunci 'enkripsi' dan 'otentikasi' akan memungkinkan Anda menyimpan informasi sesi dengan aman tanpa menggunakan tabel DB.
crantok


14

Anda akan menggunakan middleware untuk melakukan otentikasi.

Anda dapat mencoba go-http-auth untuk otentikasi dasar dan intisari dan gomniauth untuk OAuth2.

Tetapi cara mengautentikasi sangat tergantung pada aplikasi Anda.

Otentikasi memperkenalkan status / konteks ke dalam http.Handlers Anda dan telah ada beberapa diskusi tentang itu akhir-akhir ini.

Solusi yang terkenal untuk masalah konteks adalah gorilla / konteks dan konteks google yang dijelaskan di sini .

Saya membuat solusi yang lebih umum tanpa perlu negara global dalam go-on / wrap yang dapat digunakan bersama atau tanpa dua lainnya dan terintegrasi dengan baik dengan middleware konteks gratis.

wraphttpauth menyediakan integrasi go-http-auth dengan go-on / wrap.


Ada begitu banyak hal baru dengan pemula. Saya ingin tahu hal apa yang harus dimulai oleh seorang pemula. go-http-authatau gomniauthkeduanya?
Casper

Adakah yang menerapkan OAuth 1.0 di golang? Otentikasi berbasis ConsumerKey dan Rahasia?
user2888996

Bagaimana saya bisa menerapkan oAuth 1.0? Menggunakan Kunci Konsumen dan rahasia? Tolong bantu. Saya tidak mendapatkan perpustakaan yang sama.
user2888996

9

Menjawab ini pada 2018. Saya sarankan menggunakan JWT (JSON Web Token). Jawaban yang Anda tandai dipecahkan memiliki kelemahan, yang merupakan perjalanan yang dilakukan di depan (pengguna) dan belakang (server / db). Yang lebih parah jika pengguna sering melakukan permintaan yang membutuhkan auth, akan mengakibatkan permintaan membengkak dari / ke server dan database. Untuk mengatasi ini gunakan JWT yang menyimpan token di sisi pengguna yang dapat digunakan oleh pengguna kapan saja ia membutuhkan akses / permintaan. Tidak perlu perjalanan ke database dan pemrosesan server untuk memeriksa validitas token membutuhkan waktu singkat.



2

Jujur, ada banyak metode dan teknik otentikasi yang dapat Anda pasang ke aplikasi Anda dan itu tergantung pada logika dan persyaratan bisnis aplikasi.
Misalnya Oauth2, LDAP, otentikasi lokal, dll.
Jawaban saya mengasumsikan Anda mencari otentikasi lokal yang berarti Anda mengelola identitas pengguna dalam aplikasi Anda. Server harus mengekspos satu set API eksternal memungkinkan pengguna dan admin Mengelola akun dan bagaimana mereka ingin mengidentifikasi diri mereka ke Server untuk mencapai komunikasi yang dapat dipercaya. Anda akhirnya akan membuat tabel DB yang menyimpan informasi pengguna. tempat kata sandi di-hash untuk tujuan keamanan Lihat Cara menyimpan kata sandi di dalam basis data

mari asumsikan persyaratan aplikasi untuk mengautentikasi pengguna berdasarkan salah satu metode berikut:

  • otentikasi dasar (nama pengguna, kata sandi):
    Metode auth ini tergantung pada kredensial pengguna yang ditetapkan dalam header Otorisasi yang dikodekan dalam base64 dan didefinisikan dalam rfc7617 , pada dasarnya ketika aplikasi menerima pengguna meminta decode otorisasi dan re-hash password untuk membandingkannya dalam DB hash jika cocok dengan yang diautentikasi oleh pengguna, jika tidak kembalikan kode status 401 ke pengguna.

  • otentikasi berbasis sertifikat:
    Metode auth ini bergantung pada Digital Certificate untuk mengidentifikasi pengguna, dan itu dikenal sebagai auth x509, jadi ketika aplikasi menerima permintaan pengguna itu membaca sertifikat klien dan memverifikasinya yang cocok dengan sertifikat CA Root yang disediakan ke APP.

  • bearer token:
    Metode auth ini tergantung pada token Access yang berumur pendek, Token token adalah string samar, biasanya dihasilkan oleh server sebagai respons terhadap permintaan login. jadi ketika aplikasi menerima permintaan pengguna itu membaca otorisasi dan memvalidasi token untuk mengotentikasi pengguna.

Namun, saya akan merekomendasikan go-guardian untuk pustaka otentikasi yang dilakukannya melalui serangkaian metode otentikasi yang dikenal sebagai strategi. pada dasarnya Go-Guardian tidak me-mount rute atau mengasumsikan skema database tertentu, yang memaksimalkan fleksibilitas dan memungkinkan keputusan dibuat oleh pengembang.

Menyiapkan go-wali authenticator sangat mudah.

Berikut contoh lengkap metode di atas.

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
}

Pemakaian:

  • Dapatkan token:
curl  -k https://127.0.0.1:8080/login -u stackoverflow:stackoverflow
token: 90d64460d14870c08c81352a05dedd3465940a7
  • Otentikasi dengan token:
curl  -k https://127.0.0.1:8080/resource -H "Authorization: Bearer 90d64460d14870c08c81352a05dedd3465940a7"

Resource!!
  • Otentikasi dengan kredensial pengguna:
curl  -k https://127.0.0.1:8080/resource -u stackoverflow:stackoverflow

Resource!!
  • Otentikasi dengan sertifikat pengguna:
curl --cert client.pem --key client-key.pem --cacert ca.pem https://127.0.0.1:8080/resource

Resource!!

Anda dapat mengaktifkan beberapa metode otentikasi sekaligus. Anda biasanya harus menggunakan setidaknya dua metode


1

Lihatlah Labstack Echo - ia membungkus otentikasi untuk RESTful APIs dan aplikasi frontend menjadi middleware yang dapat Anda gunakan untuk melindungi rute API tertentu.

Menyiapkan otentikasi dasar, misalnya, semudah membuat subrouter baru untuk /adminrute:

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

Lihat semua opsi otentikasi middleware Labstack di sini.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.