1 回答

TA貢獻1813條經驗 獲得超2個贊
我現在創建了一個中間件來執行此操作。它非常簡單明了,我認為沒有必要使用 jwtauth 中間件。
最好的辦法似乎是使用JSON Web Key Set (JWKS)。它包含驗證 JWT 的所有信息。
package auth0
import (
"context"
"fmt"
"github.com/lestrrat-go/jwx/jwk"
"github.com/lestrrat-go/jwx/jwt"
log "github.com/sirupsen/logrus"
"net/http"
)
var (
ErrInvalidToken = fmt.Errorf("invalid token")
ErrNoToken = fmt.Errorf("no token found")
)
type middleware struct {
keySet jwk.Set
audience string
issuer string
}
type AuthUser struct {
ID string `json:"id"`
Email string `json:"email"`
}
type userKeyType string
const userKey = userKeyType("user")
type Middleware interface {
AuthenticateUser(next http.Handler) http.Handler
}
var _ Middleware = &middleware{}
func NewMiddleware(issuer string, audience string) (middleware, error) {
// TODO implement auto rotation/refresh
keySet, err := jwk.Fetch(context.Background(), fmt.Sprintf("%s.well-known/jwks.json", issuer))
if err != nil {
return middleware{}, err
}
return middleware{
keySet: keySet,
audience: audience,
issuer: issuer,
}, nil
}
func (m *middleware) AuthenticateUser(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
log.Debug("no authorization header found")
http.Error(w, ErrNoToken.Error(), http.StatusForbidden)
return
}
bearerToken := authHeader[7:]
if bearerToken == "" {
log.Error("no bearer token found")
http.Error(w, ErrNoToken.Error(), http.StatusForbidden)
return
}
token, err := jwt.Parse([]byte(bearerToken), jwt.WithKeySet(m.keySet))
if err != nil {
log.Error("error parsing token")
http.Error(w, ErrInvalidToken.Error(), http.StatusForbidden)
return
}
if err := jwt.Validate(token,
jwt.WithAudience(m.audience),
jwt.WithIssuer(m.issuer)); err != nil {
log.Error("error validating token")
http.Error(w, ErrInvalidToken.Error(), http.StatusForbidden)
return
}
emailValue, ok := token.Get("email")
if !ok {
log.Error("error no email found")
http.Error(w, ErrInvalidToken.Error(), http.StatusForbidden)
return
}
email, ok := emailValue.(string)
if !ok {
log.Error("error email not a string")
http.Error(w, ErrInvalidToken.Error(), http.StatusForbidden)
return
}
if token.Subject() == "" && email == "" {
log.Error("error no subject or email found")
http.Error(w, ErrInvalidToken.Error(), http.StatusForbidden)
return
}
ctx := context.WithValue(r.Context(), userKey, AuthUser{
ID: token.Subject(),
Email: email,
})
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func GetUserFromContext(ctx context.Context) (AuthUser, error) {
user, ok := ctx.Value(userKey).(AuthUser)
if !ok {
return AuthUser{}, fmt.Errorf("could not get user from context")
}
return user, nil
}
- 1 回答
- 0 關注
- 159 瀏覽
添加回答
舉報