Mọi người đang quản lý xác thực trong Go như thế nào? [đóng cửa]


187

Đối với những người xây dựng API RESTful và ứng dụng front-end JS trong Go, bạn quản lý xác thực như thế nào? Bạn đang sử dụng bất kỳ thư viện hoặc kỹ thuật cụ thể?

Tôi ngạc nhiên khi thấy rất ít thảo luận về điều này. Tôi ghi nhớ các câu trả lời như sau và đang cố gắng tránh phát triển việc thực hiện của riêng mình:

Mẫu xác thực trong ASP.Net

Là tất cả mọi người mã hóa giải pháp riêng của họ, riêng biệt?


5
Xác thực phụ thuộc rất nhiều vào loại ứng dụng bạn đang theo đuổi. Không có giải pháp một kích cỡ phù hợp cho tất cả. Ngoài ra, nó là một vấn đề khó giải quyết. Đây có thể là lý do tại sao bạn sẽ không tìm thấy bất kỳ tài liệu kết luận.
jimt

21
Này, cảm ơn vì đã phản hồi nhanh chóng. Hiểu, nhưng hầu hết các ngôn ngữ và khung đã đưa ra các giải pháp xác thực bao gồm các yêu cầu xác thực phổ biến nhất được chia sẻ bởi phần lớn các ứng dụng và có sự tham gia và hỗ trợ của cộng đồng rộng rãi. Tôi đồng ý rằng đó là một vấn đề khó khăn. Đừng để những người được hưởng lợi nhiều nhất từ ​​nỗ lực hợp tác? (Đây không phải là một khiếu nại, bởi vì đây là nguồn mở, nhưng nhiều hơn một quan sát rằng tất cả chúng ta đang phát minh lại bánh xe. :)
SexxLuthor

12
@jimt Thực tế rằng đó là một vấn đề khó khăn khiến việc cung cấp cho chúng ta một giải pháp hình nón mà chúng ta không thể hiểu sai càng quan trọng hơn.
tymtam

Tôi đang bỏ phiếu để đóng câu hỏi này ngoài chủ đề vì đây là câu hỏi thăm dò ý kiến.
Flimzy

Câu trả lời:


115

Câu hỏi này nhận được rất nhiều lượt xem - và có huy hiệu Câu hỏi phổ biến - vì vậy tôi biết có rất nhiều mối quan tâm tiềm ẩn trong chủ đề này và nhiều người đang hỏi chính xác điều tương tự và không tìm thấy câu trả lời trên Interwebs.

Hầu hết các kết quả thông tin có sẵn trong văn bản tương đương với điều lượn sóng tay, còn lại là một "bài tập cho người đọc." ;)

Tuy nhiên, cuối cùng tôi đã tìm thấy một ví dụ cụ thể, (hào phóng) được cung cấp bởi một thành viên trong danh sách gửi thư của golang-nut:

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

Điều này cung cấp một lược đồ được đề xuất và triển khai phía máy chủ làm cơ sở cho xác thực tùy chỉnh. Mã phía khách hàng vẫn phụ thuộc vào bạn.

(Tôi hy vọng tác giả của bài viết thấy điều này: Cảm ơn!)

Trích (và định dạng lại):


"Tôi sẽ đề xuất một cái gì đó giống như thiết kế sau đây:

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
)
  • Khi người dùng đăng nhập vào trang web của bạn thông qua POST dưới TLS, hãy xác định xem mật khẩu có hợp lệ không.
  • Sau đó, phát hành khóa phiên ngẫu nhiên, giả sử 50 ký tự trở lên và mã hóa trong Cookie an toàn.
  • Thêm khóa phiên đó vào bảng UserSession.
  • Sau đó, khi bạn gặp lại người dùng đó, trước tiên hãy nhấn vào bảng UserSession để xem liệu SessionKey có ở đó với Đăng nhập hợp lệ và LastSeenTime và Người dùng không bị xóa. Bạn có thể thiết kế nó để bộ hẹn giờ tự động xóa các hàng cũ trong UserSession. "

8
Chúng tôi có xu hướng thích một trang web độc lập ở đây tại SO, vậy bạn có phiền khi đăng giải pháp ở đây không? Chỉ trong trường hợp liên kết thay đổi đúng thời gian (thối liên kết và những gì khác ...) Khách truy cập trong tương lai có thể vui mừng về điều này.
topskip

Đó là một câu hỏi công bằng, trân trọng đặt. Cảm ơn bạn. Tôi đã bao gồm các giải pháp; Bạn có nghĩ rằng tên của tác giả cũng nên được đưa vào? (Công khai, nhưng tôi tự hỏi về nghi thức của một trong hai lựa chọn.)
SexxLuthor 14/12/14

Tôi nghĩ rằng nó là tốt như nó là. Bạn không tự nhận là "chủ sở hữu" của đoạn trích này và tôi không thể thấy rằng tác giả ban đầu của đoạn trích này yêu cầu mọi bản sao đều cần có sự ghi nhận. (Chỉ hai xu của tôi).
topskip

35
Không nên có trường "PasswordSalt" trong cơ sở dữ liệu của bạn, vì bạn nên sử dụng bcrypt làm thuật toán băm, tự động tạo ra một muối và bao gồm nó trong hàm băm được trả về. Cũng sử dụng chức năng so sánh thời gian không đổi.
0xdabbad00

4
+1 cho bcrypt. Ngoài ra, các phiên khỉ đột có khóa 'mã hóa' và 'xác thực' sẽ cho phép bạn lưu trữ thông tin phiên một cách an toàn mà không cần sử dụng bảng DB.
crantok


14

Bạn sẽ sử dụng phần mềm trung gian để thực hiện xác thực.

Bạn có thể thử go-http-auth để xác thực cơ bản và tiêu hóa và gomniauth cho OAuth2.

Nhưng làm thế nào để xác thực thực sự phụ thuộc vào ứng dụng của bạn.

Xác thực giới thiệu trạng thái / ngữ cảnh vào http.Handlers của bạn và đã có một số cuộc thảo luận về điều đó gần đây.

Các giải pháp nổi tiếng cho vấn đề bối cảnh là khỉ đột / bối cảnhbối cảnh google được mô tả ở đây .

Tôi đã thực hiện một giải pháp tổng quát hơn mà không cần trạng thái toàn cầu trong chế độ đi / gói có thể được sử dụng cùng nhau hoặc không có hai phần còn lại và tích hợp độc đáo với phần mềm trung gian không có ngữ cảnh.

wraphttpauth cung cấp tích hợp go-http-auth với go-on / quấn.


Có rất nhiều điều mới với người mới bắt đầu. Tôi tự hỏi những gì mà một người mới bắt đầu nên bắt đầu với nó. go-http-authhoặc gomniauthcả hai?
Casper

Bất cứ ai ở đây thực hiện OAuth 1.0 trong golang? Xác thực dựa trên ConsumerKey và Secret?
dùng2888996

Làm thế nào tôi có thể thực hiện oAuth 1.0? Sử dụng khóa tiêu dùng và bí mật? Xin vui lòng giúp đỡ. Tôi không nhận được bất kỳ thư viện cho cùng.
dùng2888996

10

Trả lời điều này vào năm 2018. Tôi đề nghị sử dụng JWT (JSON Web Token). Câu trả lời bạn đánh dấu đã giải quyết có nhược điểm, đó là chuyến đi mà nó đã đi trước (người dùng) và trở lại (máy chủ / db). Điều tồi tệ hơn là nếu người dùng thực hiện yêu cầu thường xuyên cần auth, sẽ dẫn đến yêu cầu cồng kềnh từ / đến máy chủ và cơ sở dữ liệu. Để giải quyết vấn đề này, hãy sử dụng JWT lưu trữ mã thông báo ở cuối người dùng mà người dùng có thể sử dụng bất cứ khi nào cần truy cập / yêu cầu. Không cần chuyến đi đến cơ sở dữ liệu và xử lý máy chủ để kiểm tra tính hợp lệ của mã thông báo mất thời gian ngắn.



2

Thành thật mà nói, có rất nhiều phương pháp và kỹ thuật xác thực mà bạn có thể cài đặt vào ứng dụng của mình và điều đó phụ thuộc vào logic và yêu cầu kinh doanh của ứng dụng.
Ví dụ: Oauth2, LDAP, xác thực cục bộ, v.v ...
Câu trả lời của tôi cho rằng bạn đang tìm kiếm xác thực cục bộ, có nghĩa là bạn quản lý danh tính người dùng trong ứng dụng của mình. Máy chủ phải hiển thị một bộ API bên ngoài cho phép người dùng và quản trị viên Quản lý tài khoản và cách họ muốn nhận dạng chính mình với Máy chủ để đạt được giao tiếp đáng tin cậy. cuối cùng bạn sẽ tạo một bảng DB chứa thông tin của người dùng. nơi mật khẩu được băm cho mục đích bảo mật Xem Cách lưu trữ mật khẩu trong cơ sở dữ liệu

giả sử các yêu cầu ứng dụng để xác thực người dùng dựa trên một trong các phương pháp sau:

  • xác thực cơ bản (tên người dùng, mật khẩu):
    Phương thức xác thực này phụ thuộc vào thông tin xác thực người dùng được đặt trong tiêu đề ủy quyền được mã hóa trong base64 và được xác định trong rfc7617 , về cơ bản khi ứng dụng nhận được yêu cầu người dùng giải mã ủy quyền và băm lại mật khẩu để so sánh nó trong DB băm nếu nó khớp với người dùng được xác thực bằng cách khác trả lại mã trạng thái 401 cho người dùng.

  • Xác thực dựa trên chứng chỉ:
    Phương thức xác thực này phụ thuộc vào Chứng chỉ kỹ thuật số để xác định người dùng và được gọi là x509 auth, vì vậy khi ứng dụng nhận được yêu cầu của người dùng, nó sẽ đọc chứng chỉ của khách hàng và xác minh nó phù hợp với chứng chỉ CA Root được cung cấp đến APP.

  • Mã thông báo mang:
    Phương thức xác thực này phụ thuộc vào mã thông báo truy cập ngắn hạn, Mã thông báo mang là một chuỗi mật mã, thường được tạo bởi máy chủ để đáp ứng yêu cầu đăng nhập. do đó, khi ứng dụng nhận được yêu cầu của người dùng, nó sẽ đọc ủy quyền và xác thực mã thông báo để xác thực người dùng.

Tuy nhiên, tôi khuyên bạn nên đi người giám hộ cho thư viện xác thực thông qua một bộ phương thức xác thực mở rộng được gọi là chiến lược. về cơ bản, Go-Guardian không gắn kết các tuyến hoặc giả định bất kỳ lược đồ cơ sở dữ liệu cụ thể nào, điều này tối đa hóa tính linh hoạt và cho phép các nhà phát triển đưa ra quyết định.

Thiết lập một trình xác thực người giám hộ đi là đơn giản.

Dưới đây là ví dụ đầy đủ của các phương pháp trên.

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
}

Sử dụng:

  • Nhận mã thông báo:
curl  -k https://127.0.0.1:8080/login -u stackoverflow:stackoverflow
token: 90d64460d14870c08c81352a05dedd3465940a7
  • Xác thực bằng mã thông báo:
curl  -k https://127.0.0.1:8080/resource -H "Authorization: Bearer 90d64460d14870c08c81352a05dedd3465940a7"

Resource!!
  • Xác thực bằng thông tin xác thực người dùng:
curl  -k https://127.0.0.1:8080/resource -u stackoverflow:stackoverflow

Resource!!
  • Xác thực bằng chứng chỉ người dùng:
curl --cert client.pem --key client-key.pem --cacert ca.pem https://127.0.0.1:8080/resource

Resource!!

Bạn có thể kích hoạt nhiều phương thức xác thực cùng một lúc. Bạn thường nên sử dụng ít nhất hai phương pháp


1

Hãy xem Labstack Echo - nó bao bọc xác thực cho các API RESTful và ứng dụng frontend vào phần mềm trung gian mà bạn có thể sử dụng để bảo vệ các tuyến API cụ thể.

Ví dụ, thiết lập xác thực cơ bản cũng đơn giản như việc tạo một nhóm con mới cho /admintuyến đường:

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

Xem tất cả các tùy chọn xác thực phần mềm trung gian của Labstack tại đây.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.