Go에서 RESTful API 및 JS 프론트 엔드 앱을 구축하는 경우 인증을 어떻게 관리하고 있습니까? 특정 라이브러리 나 기술을 사용하고 있습니까?
이것에 대해 너무 작은 토론을 발견 한 것에 놀랐습니다. 나는 다음과 같은 대답을 명심하고 내 구현을 개발하지 않도록 노력하고 있습니다.
모두 각자의 솔루션을 별도로 코딩하고 있습니까?
Go에서 RESTful API 및 JS 프론트 엔드 앱을 구축하는 경우 인증을 어떻게 관리하고 있습니까? 특정 라이브러리 나 기술을 사용하고 있습니까?
이것에 대해 너무 작은 토론을 발견 한 것에 놀랐습니다. 나는 다음과 같은 대답을 명심하고 내 구현을 개발하지 않도록 노력하고 있습니다.
모두 각자의 솔루션을 별도로 코딩하고 있습니까?
답변:
이 질문은 많은 견해를 얻었고 인기있는 질문 배지가 있으므로이 주제에 많은 관심을 가지고 있으며 많은 사람들이 정확히 똑같은 것을 묻고 인터 웹에서 답을 찾지 않습니다.
이용 가능한 대부분의 정보는 텍스트를 손으로 흔드는 것과 동일하며 "독자 운동"으로 남습니다. ;)
그러나 마지막으로 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
)
다른 가능한 해결책은 최근 메일 링 리스트 에 발표 된 Authboss입니다 . 입니다.
(이 라이브러리를 사용해 보지 않았습니다.)
또한 사용자 인증으로 웹앱을 만드는 가장 좋은 방법을 참조하십시오 .
미들웨어를 사용하여 인증을 수행합니다.
기본 및 요약 인증 및 gomniauth에 대해 go-http-auth 를 시도 할 수 있습니다. 하고 OAuth2를 위해 를 .
그러나 인증 방법은 앱에 따라 다릅니다.
인증은 http.Handlers에 상태 / 컨텍스트를 소개하며 최근에 이에 대한 논의가있었습니다.
컨텍스트 문제에 대한 잘 알려진 해결책 은 여기에 설명 된 고릴라 / 컨텍스트 및 Google 컨텍스트입니다 . 입니다.
go-on / wrap 에서 전역 상태가 필요없는보다 일반적인 솔루션을 만들었습니다. 함께 사용하거나 다른 두 가지없이 사용할 수 있으며 컨텍스트 프리 미들웨어와 잘 통합됩니다.
wraphttpauth 는 go-on / wrap과 go-http-auth의 통합을 제공합니다.
go-http-auth
또는 gomniauth
둘 다?
JWT (JSON Web Token)를 사용하는 것이 좋습니다. 해결 된 것으로 표시된 답변에는 단점이 있습니다. 이것은 front (user)와 back (server / db)을 한 여행입니다. 사용자가 인증을 필요로하는 빈번한 요청을 수행하면 서버 및 데이터베이스에 대한 요청이 부풀려집니다. 이 문제를 해결하기 위해 액세스 / 요청이 필요할 때마다 사용자가 사용할 수있는 토큰을 사용자쪽에 저장하는 JWT를 사용하십시오. 토큰 유효성을 확인하기 위해 데이터베이스 및 서버 처리로 이동할 필요가 없습니다.
쿠키 인증을 처리하기위한 또 다른 오픈 소스 패키지는 httpauth입니다. 입니다.
(내가 쓴)
솔직히 응용 프로그램에 탑재 할 수 있고 응용 프로그램 비즈니스 논리 및 요구 사항에 따라 많은 인증 방법과 기술이 있습니다.
예를 들어 Oauth2, LDAP, 로컬 인증 등이 있습니다.
제 답변은 로컬 인증을 찾고 있다고 가정합니다. 즉, 응용 프로그램에서 사용자 ID를 관리해야합니다. 서버는 사용자 및 관리자가 계정을 관리하고 신뢰할 수있는 통신을 달성하기 위해 서버에 자신을 식별하는 방법을 허용하는 외부 API 세트를 공개해야합니다. 결국 사용자 정보를 보유한 DB 테이블을 생성하게됩니다. 보안 목적으로 비밀번호가 해시되는 위치 데이터베이스에 비밀번호를 저장하는 방법을
다음 방법 중 하나를 기반으로 사용자를 인증하기 위해 앱 요구 사항을 가정합니다.
기본 인증 (사용자 이름, 비밀번호) :
이 인증 방법은 base64로 인코딩되고 rfc7617에 정의 된 권한 부여 헤더의 사용자 신임 정보 세트에 따라 다릅니다. 기본적으로 앱이 사용자에게 요청을 받으면 승인을 해독하고 비밀번호를 다시 해시하여 DB 내에서 비교합니다. 인증 된 사용자와 일치하는 경우 해시 그렇지 않으면 401 상태 코드를 사용자에게 반환합니다.
인증서 기반 인증 :
이 인증 방법은 사용자를 식별하기 위해 디지털 인증서에 의존하며이를 x509 인증이라고합니다. 따라서 앱이 사용자 요청을 받으면 클라이언트의 인증서를 읽고 제공된 CA 루트 인증서와 일치하는지 확인합니다. APP에.
베어러 토큰 :
이 인증 방법은 수명이 짧은 액세스 토큰에 따라 다릅니다. 베어러 토큰은 일반적으로 로그인 요청에 대한 응답으로 서버에서 생성되는 암호 문자열입니다. 따라서 앱이 사용자 요청을 받으면 권한을 읽고 토큰을 확인하여 사용자를 인증합니다.
그러나 전략이라고 알려진 확장 가능한 인증 방법을 통해 인증 라이브러리에 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!!
한 번에 여러 인증 방법을 활성화 할 수 있습니다. 일반적으로 두 가지 이상의 방법을 사용해야합니다
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
}))