Как люди управляют аутентификацией в Go? [закрыто]

187

Для тех, кто создает RESTful API и интерфейсные приложения JS в Go, как вы управляете аутентификацией? Используете ли вы какие-либо конкретные библиотеки или методы?

Я удивлен, обнаружив так мало дискуссий по этому поводу. Я имею в виду ответы, подобные следующим, и стараюсь избегать разработки собственной реализации:

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

Каждый кодирует свое собственное решение отдельно?

SexxLuthor
источник
5
Аутентификация во многом зависит от типа приложения, которое вы ищете. Не существует единого решения для всех. Кроме того, это сложная проблема для решения. Вероятно, поэтому вы не найдете никаких убедительных документов.
Jimt
21
Эй, спасибо за быстрый ответ. Понятно, но большинство языков и сред предлагают решения для аутентификации, которые охватывают наиболее распространенные требования аутентификации, общие для большинства приложений, и имеют широкое участие и поддержку сообщества. Я согласен, что это сложная проблема. Разве те не получают наибольшую пользу от совместных усилий? (Это не жалоба, потому что это открытый исходный код, а скорее наблюдение, что мы все заново изобретаем колесо. :)
SexxLuthor
13
@jimt Тот факт, что это сложная проблема, делает нас еще более важными, чтобы обеспечить нас, смертных, кононическим решением, которое мы не можем ошибиться.
тымтам
Я голосую, чтобы закрыть этот вопрос как не по теме, потому что это вопрос опроса.
Flimzy

Ответы:

115

Этот вопрос получает массу просмотров - и имеет значок «Популярный вопрос» - так что я знаю, что эта тема вызывает большой скрытый интерес, и многие люди задают точно такую ​​же вещь и не находят ответов на веб-страницах.

Большая часть доступной информации приводит к текстовому эквиваленту волнистости, оставленной как «упражнение для читателя». ;)

Однако я наконец нашел один конкретный пример (щедро), предоставленный участником списка рассылки 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. "
SexxLuthor
источник
8
Нам, как правило, здесь нравится SO, поэтому не могли бы вы опубликовать решение здесь? На случай, если ссылка изменится в свое время (ссылка гниет и что еще ...) Будущие посетители могут быть рады этому.
topskip
Это честный вопрос, с уважением. Спасибо. Я включил решение; Как вы думаете, имя автора также должны быть включены? (Это общедоступно, но мне интересно об этикете любого из вариантов.)
SexxLuthor
Я думаю, что это хорошо, как есть. Вы не утверждаете, что являетесь «владельцем» этого фрагмента, и я не вижу, чтобы первоначальный автор этого фрагмента требовал, чтобы для каждой копии была указана авторство. (Только мои два цента).
topskip
35
В вашей базе данных не должно быть поля PasswordSalt, потому что вы должны использовать bcrypt в качестве алгоритма хэширования, который автоматически создает соль и включает ее в возвращаемый хеш. Также используйте функцию сравнения с постоянным временем.
0xdabbad00
4
+1 для bcrypt. Кроме того, сеансы gorilla с ключами «шифрования» и «аутентификации» позволят вам безопасно хранить информацию о сеансе без использования таблицы БД.
кранток
14

Вы будете использовать промежуточное ПО для аутентификации.

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

Но способ аутентификации зависит от вашего приложения.

Аутентификация вводит состояние / контекст в ваши http.Handlers, и в последнее время обсуждается это.

Хорошо известными решениями проблемы контекста являются gorilla / context и google context, описанные здесь .

Я сделал более общее решение без необходимости глобального состояния в переходе / переносе, которое можно использовать вместе или без двух других, и красиво интегрируется с промежуточным программным обеспечением без контекста.

wraphttpauth обеспечивает интеграцию go-http-auth с включением / переносом.

metakeule
источник
Есть много нового с новичками. Интересно, что новичку следует начать с этого. go-http-authили gomniauthили оба?
Каспер
Кто-нибудь здесь реализовал OAuth 1.0 в golang? ConsumerKey и секретная аутентификация?
user2888996
Как я могу реализовать oAuth 1.0? Используя Consumer Key и секрет? Пожалуйста помоги. Я не получаю никакой библиотеки для того же.
user2888996
9

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

mfathirirhas
источник
6

Другой пакет с открытым исходным кодом для проверки подлинности с помощью файлов cookie - это httpauth .

(написано мной, кстати)

Кэмерон Литтл
источник
2

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

Давайте предположим, что приложение требует проверки подлинности пользователей на основе одного из следующих методов:

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

  • Аутентификация на основе сертификата:
    этот метод аутентификации зависит от цифрового сертификата для идентификации пользователя, и он известен как аутентификация x509, поэтому, когда приложение получает запросы пользователя, оно читает сертификат клиента и проверяет его на соответствие предоставленному корневому сертификату CA. в приложение.

  • токен носителя:
    этот метод аутентификации зависит от краткосрочных токенов доступа. Маркер носителя представляет собой загадочную строку, обычно генерируемую сервером в ответ на запрос входа в систему. поэтому, когда приложение получает запросы пользователя, оно считывает авторизацию и проверяет токен для аутентификации пользователя.

Тем не менее, я бы порекомендовал Go-Guardian для библиотеки аутентификации, что она делает с помощью расширяемого набора методов аутентификации, известных как стратегии. в основном Go-Guardian не монтирует маршруты и не принимает какую-либо конкретную схему базы данных, что максимизирует гибкость и позволяет разработчикам принимать решения.

Настройка аутентификатора Go-Guardian проста.

Вот полный пример вышеперечисленных методов.

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!!

Вы можете включить несколько методов аутентификации одновременно. Вы должны обычно использовать по крайней мере два метода

shaj13
источник
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 здесь.

Адиль Б
источник