爲了保證系統安全穩定,保護用戶數據安全,服務中通常引入身份認證手段,對用戶的請求進行安全攔截、校驗與過濾。經常使用的身份認證方式有:html
本文將在go-kit微服務中引入jwt驗證機制,實現token的簽發與驗證。關於jwt的原理再也不闡述,你們可到最後的參考文獻中查閱。簡單說下實現思路:git
dgrijalva/jwt-go
複製目錄arithmetic_circuitbreaker_demo,重命名爲arithmetic_jwt_demo,重命名register目錄爲service。github
安裝依賴的jwt第三方庫:web
go get github.com/dgrijalva/jwt-go
複製代碼
在service
目錄下建立文件jwt.go
。算法
StandardClaims
基礎上增長了UserId
、Name
兩個字段,能夠根據實際須要擴展其餘字段,如角色。keyfunc
,該方法在驗證token時做爲回調函數使用,後面會有描述。Sign
。這裏直接調用jwt第三方庫生成,爲了演示方便設置token的過時時間爲2分鐘。以下爲jwt.go
的所有代碼:docker
//secret key var secretKey = []byte("abcd1234!@#$") // ArithmeticCustomClaims 自定義聲明 type ArithmeticCustomClaims struct { UserId string `json:"userId"` Name string `json:"name"` jwt.StandardClaims } // jwtKeyFunc 返回密鑰 func jwtKeyFunc(token *jwt.Token) (interface{}, error) { return secretKey, nil } // Sign 生成token func Sign(name, uid string) (string, error) { //爲了演示方便,設置兩分鐘後過時 expAt := time.Now().Add(time.Duration(2) * time.Minute).Unix() // 建立聲明 claims := ArithmeticCustomClaims{ UserId: uid, Name: name, StandardClaims: jwt.StandardClaims{ ExpiresAt: expAt, Issuer: "system", }, } //建立token,指定加密算法爲HS256 token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) //生成token return token.SignedString(secretKey) } 複製代碼
Service層:新增登陸接口,按照go-kit的架構方式,依次:json
// Service Define a service interface type Service interface { //…… // HealthCheck Login(name, pwd string) (string, error) } func (s ArithmeticService) Login(name, pwd string) (string, error) { if name == "name" && pwd == "pwd" { token, err := Sign(name, pwd) return token, err } return "", errors.New("Your name or password dismatch") } 複製代碼
Endpoint層:新增登陸接口所需的請求和響應實體結構,編寫建立Endpoint的方法。安全
// AuthRequest type AuthRequest struct { Name string `json:"name"` Pwd string `json:"pwd"` } // AuthResponse type AuthResponse struct { Success bool `json:"success"` Token string `json:"token"` Error string `json:"error"` } func MakeAuthEndpoint(svc Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(AuthRequest) token, err := svc.Login(req.Name, req.Pwd) var resp AuthResponse if err != nil { resp = AuthResponse{ Success: err == nil, Token: token, Error: err.Error(), } } else { resp = AuthResponse{ Success: err == nil, Token: token, } } return resp, nil } } 複製代碼
Transport層:編寫decode和encode方法,新增登陸接口路由。同時,對calculate接口增長token檢測邏輯:在請求處理以前,從HTTP請求頭中讀取認證信息,若讀取成功則加入請求上下文。這裏直接使用go-kit提供的HTTPToContext方法。bash
func decodeLoginRequest(_ context.Context, r *http.Request) (interface{}, error) { var loginRequest AuthRequest if err := json.NewDecoder(r.Body).Decode(&loginRequest); err != nil { return nil, err } return loginRequest, nil } func encodeLoginResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { w.Header().Set("Content-Type", "application/json;charset=utf-8") return json.NewEncoder(w).Encode(response) } 複製代碼
增長http路由:微信
r.Methods("POST").Path("/calculate/{type}/{a}/{b}").Handler(kithttp.NewServer( endpoints.ArithmeticEndpoint, decodeArithmeticRequest, encodeArithmeticResponse, //增長了options append(options, kithttp.ServerBefore(kitjwt.HTTPToContext()))..., )) // ... r.Methods("POST").Path("/login").Handler(kithttp.NewServer( endpoints.AuthEndpoint, decodeLoginRequest, encodeLoginResponse, options..., )) 複製代碼
擴展原有ArithmeticEndpoints,增長AuthEndpoint;增長AuthEndpoint的建立邏輯代碼,爲其增長限流、鏈路追蹤等包裝。
//身份認證Endpoint authEndpoint := MakeAuthEndpoint(svc) authEndpoint = NewTokenBucketLimitterWithBuildIn(ratebucket)(authEndpoint) authEndpoint = kitzipkin.TraceEndpoint(zipkinTracer, "login-endpoint")(authEndpoint) //把算術運算Endpoint\健康檢查、登陸Endpoint封裝至ArithmeticEndpoints endpts := ArithmeticEndpoints{ ArithmeticEndpoint: calEndpoint, HealthCheckEndpoint: healthEndpoint, AuthEndpoint: authEndpoint, } 複製代碼
因爲咱們要求calculate接口只能在token有效的狀況下才可訪問,因此爲calEndpoint增長token校驗代碼(最後一行代碼,直接使用go-kit提供的中間件):
calEndpoint := MakeArithmeticEndpoint(svc) calEndpoint = NewTokenBucketLimitterWithBuildIn(ratebucket)(calEndpoint) calEndpoint = kitzipkin.TraceEndpoint(zipkinTracer, "calculate-endpoint")(calEndpoint) calEndpoint = kitjwt.NewParser(jwtKeyFunc, jwt.SigningMethodHS256, kitjwt.StandardClaimsFactory)(calEndpoint) 複製代碼
經過docker-compose
啓動consul、zipkin、hystrix-dashboard;而後啓動gateyway(指定consul地址);最後啓動service(指定consul和service地址)。
在Postman中設置POST請求http://localhost:9090/arithmetic/login,Body爲如下內容:
{ "name": "name", "pwd": "pwd" } 複製代碼
可看到如下結果:
而後,請求calculate接口,並在Header中設置Authorization,結果以下:
兩分鐘後,再次測試,會發現返回token過時。
本文結合實例,在go-kit微服務中引入jwt。新增login接口,使得calculate接口僅在token有效的狀況下才可工做。因爲jwt的認證特色,登陸成功後用戶請求的token有效性再也不依賴認證中心,相對OAuth2可大大減輕認證中心的壓力,使得微服務的水平擴展變得更加容易。
本文首發於本人微信公衆號【兮一昂吧】,歡迎掃碼關注!