Fonctionnalités principales
Go Kit fournit plusieurs composants modulaires pour construire des services robustes :
- auth - Gestion de l'authentification
- circuitbreaker - Disjoncteur pour la résilience
- endpoint - Définitoin des points d'accès aux services
- log - Système de journalisation
- metrics - Collecte de métriques
- ratelimit - Limitation du débit
- sd - Découverte de services
- tracing - Traçabilité distribuée
- transport - Couche de transport des requêtes
Middleware d'authentification
Authentification Basic Auth
func BasicAuthValidator(user, pass, realm string) endpoint.Middleware {
hashUser := convertToHash([]byte(user))
hashPass := convertToHash([]byte(pass))
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req interface{}) (interface{}, error) {
authHeader, exists := ctx.Value(httptransport.ContextKeyRequestAuthorization).(string)
if !exists {
return nil, AuthFailure{realm}
}
parsedUser, parsedPass, valid := extractBasicAuth(authHeader)
if !valid {
return nil, AuthFailure{realm}
}
hashParsedUser := convertToHash(parsedUser)
hashParsedPass := convertToHash(parsedPass)
if subtle.ConstantTimeCompare(hashParsedUser, hashUser) == 0 ||
subtle.ConstantTimeCompare(hashParsedPass, hashPass) == 0 {
return nil, AuthFailure{realm}
}
return next(ctx, req)
}
}
}
Authentification basée sur les attributs avec Casbin
func CasbinAuthorizationMiddleware(
subject string, resource interface{}, permission string,
) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req interface{}) (resp interface{}, err error) {
policyModel := ctx.Value(PolicyModelContextKey)
accessPolicy := ctx.Value(AccessPolicyContextKey)
enforcer, initErr := casbin.NewEnforcer(policyModel, accessPolicy)
if initErr != nil {
return nil, initErr
}
updatedCtx := context.WithValue(ctx, CasbinInstanceKey, enforcer)
authorized, checkErr := enforcer.Enforce(subject, resource, permission)
if checkErr != nil {
return nil, checkErr
}
if !authorized {
return nil, AccessDeniedError
}
return next(updatedCtx, req)
}
}
}
Génération et validation de tokens JWT
func JWTTokenGenerator(keyID string, secret []byte, algorithm jwt.SigningMethod, userClaims jwt.Claims) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req interface{}) (resp interface{}, err error) {
token := jwt.NewWithClaims(algorithm, userClaims)
token.Header["key_id"] = keyID
signedToken, signErr := token.SignedString(secret)
if signErr != nil {
return nil, signErr
}
ctxWithToken := context.WithValue(ctx, JWTTokenKey, signedToken)
return next(ctxWithToken, req)
}
}
}
func JWTTokenValidator(keyLookup jwt.Keyfunc, expectedAlg jwt.SigningMethod, claimsFactory ClaimsFactory) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req interface{}) (resp interface{}, err error) {
tokenValue, exists := ctx.Value(JWTTokenKey).(string)
if !exists {
return nil, MissingTokenError
}
parsedToken, parseErr := jwt.ParseWithClaims(tokenValue, claimsFactory(), func(token *jwt.Token) (interface{}, error) {
if token.Method != expectedAlg {
return nil, InvalidAlgorithmError
}
return keyLookup(token)
})
if parseErr != nil {
validationErr, isValidation := parseErr.(*jwt.ValidationError)
if isValidation {
switch {
case validationErr.Errors&jwt.ValidationErrorMalformed != 0:
return nil, MalformedTokenError
case validationErr.Errors&jwt.ValidationErrorExpired != 0:
return nil, ExpiredTokenError
case validationErr.Errors&jwt.ValidationErrorNotValidYet != 0:
return nil, InactiveTokenError
case validationErr.Inner != nil:
return nil, validationErr.Inner
}
}
return nil, parseErr
}
if !parsedToken.Valid {
return nil, InvalidTokenError
}
ctxWithClaims := context.WithValue(ctx, JWTClaimsKey, parsedToken.Claims)
return next(ctxWithClaims, req)
}
}
}
Protection par disjoncteur
Intégration avec GoBreaker
func GoBreakerMiddleware(cb *gobreaker.CircuitBreaker) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req interface{}) (interface{}, error) {
return cb.Execute(func() (interface{}, error) {
return next(ctx, req)
})
}
}
}
Disjoncteur personnalisé
func CustomBreakerMiddleware(cb breaker.Breaker) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req interface{}) (result interface{}, err error) {
if !cb.Authorize() {
return nil, breaker.ErrCircuitOpen
}
start := time.Now()
defer func() {
duration := time.Since(start)
if err == nil {
cb.RecordSuccess(duration)
} else {
cb.RecordFailure(duration)
}
}()
result, err = next(ctx, req)
return
}
}
}
Intégration avec Hystrix
func HystrixMiddleware(cmdName string) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req interface{}) (resp interface{}, err error) {
var result interface{}
execErr := hystrix.Do(cmdName, func() error {
var innerErr error
result, innerErr = next(ctx, req)
return innerErr
}, nil)
if execErr != nil {
return nil, execErr
}
return result, nil
}
}
}
Limitation de débit
Limiter avec délai
func DelayingRateLimiter(waiter Waiter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req interface{}) (interface{}, error) {
waitErr := waiter.Wait(ctx)
if waitErr != nil {
return nil, waitErr
}
return next(ctx, req)
}
}
}
Limiter avec rejet d'erreur
func RejectingRateLimiter(allowance Allower) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req interface{}) (interface{}, error) {
if !allowance.IsAllowed() {
return nil, RateLimitExceeded
}
return next(ctx, req)
}
}
}