Як люди керують автентифікацією в програмі Go? [зачинено]


187

Як ви керуєте автентифікацією для тих, хто будує API RESTful та додатки JS-інтерфейсу в Go, Чи використовуєте ви якісь конкретні бібліотеки чи методи?

Я здивований, коли зустрічаю так мало дискусій з цього приводу. Я маю на увазі відповіді на зразок наступних, і намагаюся уникати розвитку власної реалізації:

Аутентифікаційна форма в ASP.Net

Чи кожен кодує власне рішення окремо?


5
Автентифікація багато в чому залежить від типу програми, яку ви шукаєте. Не існує рішення одного розміру. Крім того, важко вирішити цю проблему. Ймовірно, тому ви не знайдете жодної переконливої ​​документації.
jimt

21
Гей, дякую за швидку відповідь. Зрозуміло, але для більшості мов та фреймворків придумано рішення для автентифікації, які охоплюють найпоширеніші вимоги до аутентифікації, якими ділиться більшість програм, і мають широку участь та підтримку спільноти. Я згоден, що це важка проблема. Чи не виграють вони найбільше від спільних зусиль? (Це не скарга, оскільки це відкрите джерело, але більше зауваження, що ми все винаходимо колесо. :)
SexxLuthor

13
@jimt Той факт, що це складна проблема, робить ще важливішим забезпечити нас смертних кононічним рішенням, яке ми не можемо помилитися.
tymtam

Я голосую, щоб закрити це питання поза темою, оскільки це опитування.
Flimzy

Відповіді:


115

Це запитання має багато переглядів - і має значок Popular Question - тому я знаю, що до цієї теми існує багато прихованого інтересу, і багато людей задають саме те саме і не знаходять відповідей на Інтервебесі.

Більшість наявної інформації призводить до текстуального еквіваленту ручної хвилястої речі, залишеної як "вправа для читача". ;)

Однак я нарешті знайшов один конкретний приклад (щедро), наданий членом списку розсилки golang-nuts:

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

Це забезпечує запропоновану схему та реалізацію на стороні сервера як основу для власної автентифікації. Клієнтський код все ще залежить від вас.

(Я сподіваюся, автор публікації бачить це: Дякую!)

Визначені (і переформатовані):


"Я б запропонував щось на зразок такої конструкції:

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
)
  • Коли користувач заходить на ваш сайт через POST під TLS, визначте, чи пароль дійсний.
  • Потім випустіть випадковий ключ сеансу, скажімо, 50 або більше символів криптовалюти та інше в захищеному файлі cookie.
  • Додайте цей ключ сеансу до таблиці UserSession.
  • Потім, коли ви знову побачите цього користувача, спочатку натисніть на таблицю UserSession, щоб побачити, чи є там SessionKey з дійсними LoginTime та LastSeenTime, а користувач не видалений. Ви можете спроектувати його, щоб таймер автоматично очистив старі рядки в UserSession. "

8
Нам, як правило, подобається автономний веб-сайт тут, так що ви не хочете розмістити рішення і тут? На всякий випадок, якщо посилання зміниться у свій час (гниль посилань та що ще ...) Майбутні відвідувачі можуть раді цього.
topskip

Це справедливе питання, з повагою поставлене. Дякую. Я включив рішення; чи вважаєте ви, що ім’я автора також має бути вказане? (Це публічно, але мені цікаво етикет будь-якого варіанту.)
SexxLuthor

Я думаю, що це добре як є. Ви не претендуєте на те, що ви є власником цього фрагмента, і я не можу побачити, що оригінальний автор цього фрагмента вимагає, щоб кожна копія потребувала атрибуції. (Тільки два мої центи).
topskip

35
У вашій базі даних не повинно бути поля "PasswordSalt", оскільки ви повинні використовувати bcrypt як свій алгоритм хешування, який автоматично створює сіль і включає її у повернений хеш. Також використовуйте постійну функцію порівняння часу.
0xdabbad00

4
+1 для bcrypt. Крім того, сеанси горили з її ключами "шифрування" та "автентифікації" дозволять надійно зберігати інформацію про сеанси без використання таблиці БД.
кранток


14

Ви б використовували проміжне програмне забезпечення для аутентифікації.

Ви можете спробувати go-http-auth для основної та дайджестної аутентифікації та gomniauth для OAuth2.

Але як автентифікувати дійсно, залежить від вашої програми.

Перевірка автентичності вводить стан / контекст у ваші http.Handlers, і останнім часом були дискусії з цього приводу.

Добре відомими рішеннями контекстної проблеми є описані тут горила / контекст та контекст google .

Я прийняв більш загальне рішення, не потребуючи глобального стану в роботі go / on / wrap, яке можна використовувати разом або без двох інших і добре поєднується з контекстним безкоштовним середнім програмним забезпеченням.

wraphttpauth забезпечує інтеграцію go-http-auth з go-on / wrap.


У новачків так багато нового. Цікаво, яку саме справу повинен починати новачок. go-http-authабо gomniauthабо обоє?
Каспер

Хтось тут реалізував OAuth 1.0 у голанг? Автентифікація на основі ConsumerKey та Secret?
користувач2888996

Як я можу реалізувати oAuth 1.0? Використання споживчого ключа та секрету? Будь ласка, допоможіть. Я не отримую жодної бібліотеки для того ж.
користувач2888996

9

Відповідаючи на це у 2018 році. Я пропоную використовувати JWT (JSON Web Token). Відповідь, яку ви позначили вирішеною, має недолік - це подорож, яку вона здійснила спереду (користувач) і назад (сервер / db). Що ще гірше, якщо користувач часто звертається із запитом на аутентифікацію, це призведе до роздуття запиту з / на сервер та базу даних. Щоб вирішити це, використовуйте JWT, який зберігає маркер в кінці користувача, який може використовуватись користувачем будь-коли, коли йому потрібен доступ / запит. Немає необхідності відвідувати базу даних та обробку сервера, щоб перевірити дійсність маркера.


6

Ще один пакет з відкритим кодом для обробки автентифікації за допомогою файлів cookie є httpauth .

(написано мною, до речі)


2

Чесно кажучи, існує багато методів та методів аутентифікації, які можна встановити у вашу програму, і це залежить від логіки та вимог бізнесу.
Наприклад, Oauth2, LDAP, локальна автентифікація тощо.
Моя відповідь передбачає, що ви шукаєте локальну автентифікацію, що означає, що ви керуєте особами користувача у вашій програмі. Сервер повинен відкривати набір зовнішніх API, що дозволяє користувачам і адміністраторам Керувати обліковими записами та як вони хочуть ідентифікувати себе на Сервері для досягнення надійного зв'язку. ви в кінцевому підсумку створите таблицю БД, що містить інформацію про користувача. де пароль хешований з метою безпеки Див Як зберігати пароль у базі даних

дозвольте припустити вимоги програми для автентифікації користувачів на основі одного з таких методів:

  • основна автентифікація (ім'я користувача, пароль):
    Цей метод автентифікації залежить від наборів даних користувачів у заголовку авторизації, закодованого в base64 та визначеному в rfc7617 , в основному, коли додаток отримує, користувач запитує його декодує авторизацію та повторно хеш-пароль для порівняння його в БД хеш, якщо він відповідний користувачеві, інакше повертає 401 код статусу користувачеві.

  • аутентифікація на основі сертифікатів:
    Цей метод автентифікації залежить від цифрового сертифіката для ідентифікації користувача, і він відомий як x509 auth, тому коли програма отримує запит від користувача, вона зчитує сертифікат клієнта і перевіряє, чи відповідає сертифікат кореневого сертифіката CA, що надається до APP.

  • маркер носія:
    Цей метод автентифікації залежить від короткотривалих маркерів доступу, маркер носія - це криптований рядок, який зазвичай генерується сервером у відповідь на запит на вхід. тому, коли додаток отримує запит користувача, воно зчитує авторизацію та перевіряє маркер для автентифікації користувача.

Однак я б рекомендував go-guardian для бібліотеки аутентифікації, яку він робить через розширюваний набір методів аутентифікації, відомих як стратегії. в основному Go-Guardian не монтує маршрути і не передбачає будь-якої конкретної схеми бази даних, що забезпечує максимальну гнучкість і дозволяє розробникам приймати рішення.

Налаштування автентифікатора Go-Guard є простим.

Ось повний приклад перерахованих вище методів.

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
}

Використання:

  • Отримати маркер:
curl  -k https://127.0.0.1:8080/login -u stackoverflow:stackoverflow
token: 90d64460d14870c08c81352a05dedd3465940a7
  • Аутентифікувати за допомогою маркера:
curl  -k https://127.0.0.1:8080/resource -H "Authorization: Bearer 90d64460d14870c08c81352a05dedd3465940a7"

Resource!!
  • Аутентифікація за допомогою облікових даних користувача:
curl  -k https://127.0.0.1:8080/resource -u stackoverflow:stackoverflow

Resource!!
  • Аутентифікат із сертифікатом користувача:
curl --cert client.pem --key client-key.pem --cacert ca.pem https://127.0.0.1:8080/resource

Resource!!

Ви можете ввімкнути відразу кілька методів аутентифікації. Зазвичай слід використовувати як мінімум два методи


1

Погляньте на Labstack Echo - він обертає автентифікацію для RESTful API та інтерфейсних програм у середнє програмне забезпечення, яке ви можете використовувати для захисту конкретних маршрутів API.

Наприклад, налаштування базової аутентифікації настільки ж просто, як і створення нового підпрограму для /adminмаршруту:

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

Ознайомтесь з усіма параметрами автентифікації проміжного програмного забезпечення Labstack тут.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.