go-kit/kit

基本功能

auth -- 校验

circuitbreaker -- 熔断

endpoint -- 服务

log -- 日志

metrics -- 监控

ratelimit -- 限流

sd -- 服务发现

tracing -- 跟踪

transport -- 请求

auth


// basic auth
// AuthMiddleware returns a Basic Authentication middleware for a particular user and password.
func AuthMiddleware(requiredUser, requiredPassword, realm string) endpoint.Middleware {
        requiredUserBytes := toHashSlice([]byte(requiredUser))
        requiredPasswordBytes := toHashSlice([]byte(requiredPassword))

        return func(next endpoint.Endpoint) endpoint.Endpoint {
                return func(ctx context.Context, request interface{}) (interface{}, error) {

                        // 获取加密信息
                        auth, ok := ctx.Value(httptransport.ContextKeyRequestAuthorization).(string)
                        if !ok {
                                return nil, AuthError{realm}
                        }

                        // 解析用户名及密码
                        givenUser, givenPassword, ok := parseBasicAuth(auth)
                        if !ok {
                                return nil, AuthError{realm}
                        }

                        givenUserBytes := toHashSlice(givenUser)
                        givenPasswordBytes := toHashSlice(givenPassword)

                        // 判断用户名及密码
                        if subtle.ConstantTimeCompare(givenUserBytes, requiredUserBytes) == 0 ||
                                subtle.ConstantTimeCompare(givenPasswordBytes, requiredPasswordBytes) == 0 {
                                return nil, AuthError{realm}
                        }

                        return next(ctx, request)
                }
        }
}

// casbin auth (abac)

// NewEnforcer checks whether the subject is authorized to do the specified
// action on the given object. If a valid access control model and policy
// is given, then the generated casbin Enforcer is stored in the context
// with CasbinEnforcer as the key.
func NewEnforcer(
        subject string, object interface{}, action string,
) endpoint.Middleware {
        return func(next endpoint.Endpoint) endpoint.Endpoint {
                return func(ctx context.Context, request interface{}) (response interface{}, err error) {
                        
                        // 模型定义
                        casbinModel := ctx.Value(CasbinModelContextKey)

                        // 访问控制策略
                        casbinPolicy := ctx.Value(CasbinPolicyContextKey)
                        enforcer, err := stdcasbin.NewEnforcer(casbinModel, casbinPolicy)
                        if err != nil {
                                return nil, err
                        }

                        ctx = context.WithValue(ctx, CasbinEnforcerContextKey, enforcer)

                        // 校验 subject,object,action
                        ok, err := enforcer.Enforce(subject, object, action)
                        if err != nil {
                                return nil, err
                        }
                        if !ok {
                                return nil, ErrUnauthorized
                        }

                        return next(ctx, request)
                }
        }
}


// jwt auth

// NewSigner creates a new JWT token generating middleware, specifying key ID,
// signing string, signing method and the claims you would like it to contain.
// Tokens are signed with a Key ID header (kid) which is useful for determining
// the key to use for parsing. Particularly useful for clients.
func NewSigner(kid string, key []byte, method jwt.SigningMethod, claims jwt.Claims) endpoint.Middleware {
        return func(next endpoint.Endpoint) endpoint.Endpoint {
                return func(ctx context.Context, request interface{}) (response interface{}, err error) {
                        token := jwt.NewWithClaims(method, claims)
                        token.Header["kid"] = kid

                        // Sign and get the complete encoded token as a string using the secret
                        tokenString, err := token.SignedString(key)
                        if err != nil {
                                return nil, err
                        }
                        ctx = context.WithValue(ctx, JWTTokenContextKey, tokenString)

                        return next(ctx, request)
                }
        }
}

// NewParser creates a new JWT token parsing middleware, specifying a
// jwt.Keyfunc interface, the signing method and the claims type to be used. NewParser
// adds the resulting claims to endpoint context or returns error on invalid token.
// Particularly useful for servers.
func NewParser(keyFunc jwt.Keyfunc, method jwt.SigningMethod, newClaims ClaimsFactory) endpoint.Middleware {
        return func(next endpoint.Endpoint) endpoint.Endpoint {
                return func(ctx context.Context, request interface{}) (response interface{}, err error) {
                        // tokenString is stored in the context from the transport handlers.
                        tokenString, ok := ctx.Value(JWTTokenContextKey).(string)
                        if !ok {
                                return nil, ErrTokenContextMissing
                        }

                        // Parse takes the token string and a function for looking up the
                        // key. The latter is especially useful if you use multiple keys
                        // for your application.  The standard is to use 'kid' in the head
                        // of the token to identify which key to use, but the parsed token
                        // (head and claims) is provided to the callback, providing
                        // flexibility.
                        token, err := jwt.ParseWithClaims(tokenString, newClaims(), func(token *jwt.Token) (interface{}, error) {
                                // Don't forget to validate the alg is what you expect:
                                if token.Method != method {
                                        return nil, ErrUnexpectedSigningMethod
                                }

                                return keyFunc(token)
                        })
                        if err != nil {
                                if e, ok := err.(*jwt.ValidationError); ok {
                                        switch {
                                        case e.Errors&jwt.ValidationErrorMalformed != 0:
                                                // Token is malformed
                                                return nil, ErrTokenMalformed
                                        case e.Errors&jwt.ValidationErrorExpired != 0:
                                                // Token is expired
                                                return nil, ErrTokenExpired
                                        case e.Errors&jwt.ValidationErrorNotValidYet != 0:
                                                // Token is not active yet
                                                return nil, ErrTokenNotActive
                                        case e.Inner != nil:
                                                // report e.Inner
                                                return nil, e.Inner
                                        }
                                        // We have a ValidationError but have no specific Go kit error for it.
                                        // Fall through to return original error.
                                }
                                return nil, err
                        }

                        if !token.Valid {
                                return nil, ErrTokenInvalid
                        }

                        ctx = context.WithValue(ctx, JWTClaimsContextKey, token.Claims)

                        return next(ctx, request)
                }
        }
}

circuitbreaker

func Gobreaker(cb *gobreaker.CircuitBreaker) endpoint.Middleware {
        return func(next endpoint.Endpoint) endpoint.Endpoint {
                return func(ctx context.Context, request interface{}) (interface{}, error) {
                        return cb.Execute(func() (interface{}, error) { return next(ctx, request) })
                }
        }
}

func HandyBreaker(cb breaker.Breaker) endpoint.Middleware {
        return func(next endpoint.Endpoint) endpoint.Endpoint {
                return func(ctx context.Context, request interface{}) (response interface{}, err error) {
                        if !cb.Allow() {
                                return nil, breaker.ErrCircuitOpen
                        }

                        defer func(begin time.Time) {
                                if err == nil {
                                        cb.Success(time.Since(begin))
                                } else {
                                        cb.Failure(time.Since(begin))
                                }
                        }(time.Now())

                        response, err = next(ctx, request)
                        return
                }
        }
}

func Hystrix(commandName string) endpoint.Middleware {
        return func(next endpoint.Endpoint) endpoint.Endpoint {
                return func(ctx context.Context, request interface{}) (response interface{}, err error) {
                        var resp interface{}
                        if err := hystrix.Do(commandName, func() (err error) {
                                resp, err = next(ctx, request)
                                return err
                        }, nil); err != nil {
                                return nil, err
                        }
                        return resp, nil
                }
        }
}

ratelimit

func NewDelayingLimiter(limit Waiter) endpoint.Middleware {
        return func(next endpoint.Endpoint) endpoint.Endpoint {
                return func(ctx context.Context, request interface{}) (interface{}, error) {
                        if err := limit.Wait(ctx); err != nil {
                                return nil, err
                        }
                        return next(ctx, request)
                }
        }
}

// NewErroringLimiter returns an endpoint.Middleware that acts as a rate
// limiter. Requests that would exceed the
// maximum request rate are simply rejected with an error.
func NewErroringLimiter(limit Allower) endpoint.Middleware {
        return func(next endpoint.Endpoint) endpoint.Endpoint {
                return func(ctx context.Context, request interface{}) (interface{}, error) {
                        if !limit.Allow() {
                                return nil, ErrLimited
                        }
                        return next(ctx, request)
                }
        }
}