- Оригинальный адрес:Build your Own OAuth2 Server in Go: Client Credentials Grant Flow
- Оригинальный автор:Cyan Tarek
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:shixi-li
- Корректор:JackEggie, LucaslEliane
Привет, в сегодняшней статье я покажу вам, как создать собственный сервер OAuth2 для каждого, как у таких компаний, как google, facebook и github.
Это может быть полезно, если вы хотите создавать общедоступные или частные API для производственного использования. Итак, давайте начнем сейчас.
Что такое OAuth2?
Открытая авторизация версии 2.0 называется OAuth2. Это протокол или структура для защиты веб-служб RESTful. OAuth2 очень мощный. Из-за надежной защиты OAuth2 большинство REST API теперь защищены с помощью OAuth2.
OAuth2 состоит из двух частей.
-
клиент
-
Сервер
клиент OAuth2
Если вы знакомы с интерфейсом, вы поймете, что я собираюсь сказать. Знакомо это или нет, позвольте мне рассказать историю этого изображения.
Вы создаете пользовательское приложение, которое работает с пользовательским репозиторием github. Например: инструменты CI, такие как TravisCI, CircleCI и Drone.
Но учетная запись пользователя на github защищена, и никто не имеет к ней доступа, если владелец этого не хочет. Так как же эти инструменты CI получают доступ к учетным записям и репозиториям пользователей на github?
Это на самом деле довольно просто.
Ваше приложение запросит у пользователя
«Чтобы работать с нашим сервисом, нам нужен доступ для чтения к вашему репозиторию github. Вы согласны?»
Тогда пользователь скажет
«Я согласен. Вы можете делать то, что вам нужно».
Тогда ваше заявление запрашивает разрешение Management GitHub Github для получения доступа к этому конкретному пользователю. GitHub проверит, правда ли это и попросить пользователя для авторизации. После того, как GitHub через временный токен будет отправлен клиенту.
Теперь, когда вашему приложению требуется доступ к github после аутентификации и авторизации, ему нужно передать этот токен в середине запроса, и после того, как github его получит, он будет думать:
«Эй, этот токен доступа выглядит знакомым. Мы должны были дать его вам раньше. Хорошо, вы можете получить к нему доступ».
Это долгий процесс. Но времена изменились, теперь вам не нужно каждый раз обращаться в центр авторизации github (конечно, нам никогда не нужно). Все можно сделать автоматически.
Но как?
Вот диаграмма последовательности UML, соответствующая тому, что я обсуждал в последние несколько минут. является соответствующим графическим представлением.
На изображении выше мы можем найти несколько важных вещей.
OAuth2 имеет 4 роли:
-
Пользователь — пользователь, который в конечном итоге будет использовать ваше приложение.
-
Клиент — это приложение, которое вы создаете, которое будет использовать учетную запись github, которую будет использовать пользователь.
-
Сервер аутентификации — этот сервер в основном обрабатывает транзакции, связанные с OAuth.
-
Сервер ресурсов — на этом сервере есть защищенные ресурсы. Например гитхаб
Клиент отправляет запрос OAuth2 на сервер аутентификации от имени пользователя.
Создание клиента OAuth2 не является ни простым, ни сложным. Звучит интересно, верно? Мы сделаем это в действии в следующем разделе.
Но в этом разделе мы собираемся взглянуть на другую сторону мира. Мы создадим собственный сервер OAuth2. Это не легко, но весело.
Вы готовы? Давайте начнем
OAuth2-сервер
ты можешь спросить меня
«Циан, подожди, зачем ты строишь сервер OAuth2?»
друг, ты забыл? Я говорил это раньше. Ну, позвольте мне сказать вам снова.
Представьте, что вы создаете замечательное приложение, предоставляющее точную информацию о погоде (существует множество API такого типа). Теперь вы хотите сделать его открытым для использования публикой или хотите заработать на этом деньги.
Но несмотря ни на что, вам необходимо защитить свои ресурсы от несанкционированного доступа или вредоносных атак. Поэтому вам необходимо защитить свои ресурсы API. Тогда вам нужно использовать OAuth2 здесь. Правильно!
Как видно из рисунка выше, сервер аутентификации необходимо разместить перед сервером ресурсов REST API. Это то, что мы собираемся обсудить. Этот сервер аутентификации должен быть построен в соответствии со спецификацией OAuth2. Тогда мы станем гитхабом на первой картинке, хахахаха шучу.
Основная цель сервера OAuth2 — предоставить клиентам токены доступа. Вот почему серверы OAuth2 также называют поставщиками OAuth2, поскольку они могут предоставлять токены.
Это все для этого объяснения.
Существует 4 различных режима сервера OAuth2 в зависимости от потока аутентификации:
-
Режим кода авторизации
-
Неявный режим предоставления
-
Режим аутентификации клиента
-
режим пароля
Если вы хотите узнать больше об OAuth2, см.здесьЗамечательная статья.
В этой статье мы будем использоватьРежим аутентификации клиента. Давайте посмотрим поближе.
Поток авторизации клиентских учетных данных на основе сервера
Есть несколько вещей, которые нам нужно понять при создании потока авторизации учетных данных клиента на основе сервера OAuth2.
В этом типе авторизации нет взаимодействия с пользователем (т.е. нет регистрации, входа в систему). Вместо этого необходимы две вещи, и ониID клиентаисекрет клиента. С этими двумя вещами мы можем получитьтокен доступа. Клиент — это стороннее приложение. Этот тип авторизации удобен и подходит, когда вы хотите получить доступ к серверу ресурсов без пользовательского механизма или только через клиентское приложение.
Это соответствующая диаграмма последовательности UML.
кодирование
Чтобы построить этот проект, нам нужно положиться на отличный языковой пакет Go.
Во-первых, нам нужно разработать простую службу API в качестве сервера ресурсов.
package main
import (
"log"
"net/http"
)
func main() {
http.HandleFunc("/protected", validateToken(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, I'm protected"))
}, srv))
log.Fatal(http.ListenAndServe(":9096", nil))
}
Запустите службу и отправьте запросы Get наhttp://localhost:9096/protected
Вы получите ответ.
Под какой защитой находится этот сервис?
Даже если имя этого интерфейса определено как защищенное, его может запросить любой желающий. Нам нужно защитить этот интерфейс с помощью OAuth2.
Теперь мы собираемся написать собственный сервис авторизации.
маршрутизация
-
/credentialsИспользуется для выдачи учетных данных клиента (идентификатор клиента и секрет клиента)
-
/tokenВыпустить токен с учетными данными клиента
Нам нужно реализовать эти два маршрута.
Вот первоначальная настройка
package main
import (
"encoding/json"
"fmt"
"github.com/google/uuid"
"gopkg.in/oauth2.v3/models"
"log"
"net/http"
"time"
"gopkg.in/oauth2.v3/errors"
"gopkg.in/oauth2.v3/manage"
"gopkg.in/oauth2.v3/server"
"gopkg.in/oauth2.v3/store"
)
func main() {
manager := manage.NewDefaultManager()
manager.SetAuthorizeCodeTokenCfg(manage.DefaultAuthorizeCodeTokenCfg)
manager.MustTokenStorage(store.NewMemoryTokenStore())
clientStore := store.NewClientStore()
manager.MapClientStorage(clientStore)
srv := server.NewDefaultServer(manager)
srv.SetAllowGetAccessRequest(true)
srv.SetClientInfoHandler(server.ClientFormHandler)
manager.SetRefreshTokenCfg(manage.DefaultRefreshTokenCfg)
srv.SetInternalErrorHandler(func(err error) (re *errors.Response) {
log.Println("Internal Error:", err.Error())
return
})
srv.SetResponseErrorHandler(func(re *errors.Response) {
log.Println("Response Error:", re.Error.Error())
})
http.HandleFunc("/protected", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, I'm protected"))
})
log.Fatal(http.ListenAndServe(":9096", nil))
}
Здесь мы создаем менеджер клиентского хранилища и сам сервис аутентификации.
вот/credentialsРеализация маршрутизации:
http.HandleFunc("/credentials", func(w http.ResponseWriter, r *http.Request) {
clientId := uuid.New().String()[:8]
clientSecret := uuid.New().String()[:8]
err := clientStore.Set(clientId, &models.Client{
ID: clientId,
Secret: clientSecret,
Domain: "http://localhost:9094",
})
if err != nil {
fmt.Println(err.Error())
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"CLIENT_ID": clientId, "CLIENT_SECRET": clientSecret})
})
Он создает две случайные строки, одна из которых является идентификатором клиента, а другая — секретом клиента. и сохранить их в хранилище клиента. Тогда ответ будет возвращен. Вот и все. Здесь мы используем хранилище в памяти, но мы также можем хранить их в redis, mongodb, postgres и т. д.
вот/tokenРеализация маршрутизации:
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
srv.HandleTokenRequest(w, r)
})
Это очень просто. Он передает запрос и ответ соответствующему обработчику, чтобы сервер мог декодировать все необходимые данные в запросе.
Итак, вот наш общий код:
package main
import (
"encoding/json"
"fmt"
"github.com/google/uuid"
"gopkg.in/oauth2.v3/models"
"log"
"net/http"
"time"
"gopkg.in/oauth2.v3/errors"
"gopkg.in/oauth2.v3/manage"
"gopkg.in/oauth2.v3/server"
"gopkg.in/oauth2.v3/store"
)
func main() {
manager := manage.NewDefaultManager()
manager.SetAuthorizeCodeTokenCfg(manage.DefaultAuthorizeCodeTokenCfg)
manager.MustTokenStorage(store.NewMemoryTokenStore())
clientStore := store.NewClientStore()
manager.MapClientStorage(clientStore)
srv := server.NewDefaultServer(manager)
srv.SetAllowGetAccessRequest(true)
srv.SetClientInfoHandler(server.ClientFormHandler)
manager.SetRefreshTokenCfg(manage.DefaultRefreshTokenCfg)
srv.SetInternalErrorHandler(func(err error) (re *errors.Response) {
log.Println("Internal Error:", err.Error())
return
})
srv.SetResponseErrorHandler(func(re *errors.Response) {
log.Println("Response Error:", re.Error.Error())
})
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
srv.HandleTokenRequest(w, r)
})
http.HandleFunc("/credentials", func(w http.ResponseWriter, r *http.Request) {
clientId := uuid.New().String()[:8]
clientSecret := uuid.New().String()[:8]
err := clientStore.Set(clientId, &models.Client{
ID: clientId,
Secret: clientSecret,
Domain: "http://localhost:9094",
})
if err != nil {
fmt.Println(err.Error())
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"CLIENT_ID": clientId, "CLIENT_SECRET": clientSecret})
})
http.HandleFunc("/protected", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, I'm protected"))
})
log.Fatal(http.ListenAndServe(":9096", nil))
}
запустите этот код и получитеhttp://localhost:9096/credentialsМаршрут для регистрации и получения идентификатора клиента и секрета клиента.
теперь перейдите по этой ссылкеhttp://localhost:9096/token?grant_type=client_credentials&client_id=2e14f7dd&client_secret=c729e9d0&scope=all
Вы можете получить токен авторизации со сроком действия и некоторой другой информацией.
Теперь у нас есть токен авторизации. Но наш маршрут /protected по-прежнему не защищен. Нам нужно настроить метод для проверки того, что каждый клиентский запрос содержит действительный токен. Если это так, мы можем авторизовать клиента. В противном случае авторизация не может быть предоставлена.
Мы можем сделать это с помощью промежуточного программного обеспечения.
Написание промежуточного программного обеспечения на golang может быть забавным, если вы знаете, что делаете. Ниже приведен код промежуточного программного обеспечения:
func validateToken(f http.HandlerFunc, srv *server.Server) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := srv.ValidationBearerToken(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
f.ServeHTTP(w, r)
})
}
Это проверит, имеет ли запрос действительный токен, и предпримет соответствующие действия.
Теперь нам нужно использовать шаблон адаптера/декоратора, чтобы поместить промежуточное ПО перед нашим маршрутом /protected.
http.HandleFunc("/protected", validateToken(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, I'm protected"))
}, srv))
Теперь весь код выглядит так:
package main
import (
"encoding/json"
"fmt"
"github.com/google/uuid"
"gopkg.in/oauth2.v3/models"
"log"
"net/http"
"time"
"gopkg.in/oauth2.v3/errors"
"gopkg.in/oauth2.v3/manage"
"gopkg.in/oauth2.v3/server"
"gopkg.in/oauth2.v3/store"
)
func main() {
manager := manage.NewDefaultManager()
manager.SetAuthorizeCodeTokenCfg(manage.DefaultAuthorizeCodeTokenCfg)
// token memory store
manager.MustTokenStorage(store.NewMemoryTokenStore())
// client memory store
clientStore := store.NewClientStore()
manager.MapClientStorage(clientStore)
srv := server.NewDefaultServer(manager)
srv.SetAllowGetAccessRequest(true)
srv.SetClientInfoHandler(server.ClientFormHandler)
manager.SetRefreshTokenCfg(manage.DefaultRefreshTokenCfg)
srv.SetInternalErrorHandler(func(err error) (re *errors.Response) {
log.Println("Internal Error:", err.Error())
return
})
srv.SetResponseErrorHandler(func(re *errors.Response) {
log.Println("Response Error:", re.Error.Error())
})
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
srv.HandleTokenRequest(w, r)
})
http.HandleFunc("/credentials", func(w http.ResponseWriter, r *http.Request) {
clientId := uuid.New().String()[:8]
clientSecret := uuid.New().String()[:8]
err := clientStore.Set(clientId, &models.Client{
ID: clientId,
Secret: clientSecret,
Domain: "http://localhost:9094",
})
if err != nil {
fmt.Println(err.Error())
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"CLIENT_ID": clientId, "CLIENT_SECRET": clientSecret})
})
http.HandleFunc("/protected", validateToken(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, I'm protected"))
}, srv))
log.Fatal(http.ListenAndServe(":9096", nil))
}
func validateToken(f http.HandlerFunc, srv *server.Server) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := srv.ValidationBearerToken(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
f.ServeHTTP(w, r)
})
}
Теперь запустите службу с URL-адресом безтокен доступадоступ без/protectedинтерфейс. или попытаться использовать неправильныйтокен доступа. Служба аутентификации заблокирует вас в любом случае.
Теперь получить снова с сервераИнформация о сертификации and токен доступаи отправить запрос на защищенный интерфейс:
http://localhost:9096/test?access_token=YOUR_ACCESS_TOKEN
правильно! Теперь у вас есть разрешение на доступ.
Теперь мы узнали, как настроить собственный сервер OAuth2 с помощью Go.
в следующем разделе. Мы создадим собственный клиент OAuth2 в Go. И в последней части мы создадим свой собственный на основе входа в систему и авторизации.Режим кода авторизации на основе сервера.
Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.