v1.0.3

中介層

最後更新


go-jwt 提供 Gin 與 net/http 兩種中介層(Middleware),都以 Verify() 驗證請求,成功時把 *Auth 交給後續 handler。

行為


項目GinMiddleware()HTTPMiddleware(next)
驗證Verify(c.Writer, c.Request)Verify(w, r)
失敗回應c.JSON(StatusCode, {"error": ...}) 後 c.Abort()Content-Type: application/json、StatusCode、{"error": ...},不呼叫 next
成功時存放c.Set("user", *Auth)context.WithValue(r.Context(), "user", *Auth)
取出GetAuthDataFromGinContext(c)GetAuthDataFromHTTPRequest(r)

失敗回應只含 error,不含 ErrorTag;需要依錯誤類型分流時,改在自己的 handler 直接呼叫 Verify()。
刷新發生在中介層內部,新 Token 透過 Set-Cookie(以及僅重簽時的 X-New-Access-Token)隨後續 handler 的回應一起送出。

Gin


  1. package main
  2. import (
  3. "log"
  4. "net/http"
  5. "github.com/gin-gonic/gin"
  6. goJwt "github.com/pardnchiu/go-jwt/core"
  7. )
  8. func main() {
  9. auth, err := goJwt.New(goJwt.Config{
  10. Redis: goJwt.Redis{Host: "localhost", Port: 6379},
  11. })
  12. if err != nil {
  13. log.Fatalf("初始化 go-jwt 失敗: %v", err)
  14. }
  15. defer auth.Close()
  16. r := gin.Default()
  17. r.POST("/login", func(c *gin.Context) {
  18. result := auth.Create(c.Writer, c.Request, &goJwt.Auth{ID: "u_001", Name: "Pardn"})
  19. if !result.Success {
  20. c.JSON(result.StatusCode, gin.H{"error": result.Error, "tag": result.ErrorTag})
  21. return
  22. }
  23. c.JSON(http.StatusOK, result.Token)
  24. })
  25. api := r.Group("/api", auth.GinMiddleware())
  26. api.GET("/me", func(c *gin.Context) {
  27. user, ok := goJwt.GetAuthDataFromGinContext(c)
  28. if !ok {
  29. c.AbortWithStatus(http.StatusUnauthorized)
  30. return
  31. }
  32. c.JSON(http.StatusOK, user)
  33. })
  34. if err := r.Run(":8080"); err != nil {
  35. log.Fatal(err)
  36. }
  37. }

net/http


  1. mux := http.NewServeMux()
  2. mux.Handle("GET /me", auth.HTTPMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3. user, ok := goJwt.GetAuthDataFromHTTPRequest(r)
  4. if !ok {
  5. http.Error(w, "unauthorized", http.StatusUnauthorized)
  6. return
  7. }
  8. w.Header().Set("Content-Type", "application/json")
  9. json.NewEncoder(w).Encode(user)
  10. })))

Context key


HTTPMiddleware 以字串 "user" 作為 context key。若應用程式其他地方也用字串 "user" 存值,會互相覆蓋;一律透過 GetAuthDataFromHTTPRequest() 讀取,它會在型別不是 *Auth 時回傳 false。

依角色或範圍授權


中介層只負責身分驗證。Auth.Role、Auth.Scope、Auth.Level 由應用程式自行判斷:
  1. func requireScope(scope string, next http.Handler) http.Handler {
  2. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3. user, ok := goJwt.GetAuthDataFromHTTPRequest(r)
  4. if !ok || !slices.Contains(user.Scope, scope) {
  5. http.Error(w, "forbidden", http.StatusForbidden)
  6. return
  7. }
  8. next.ServeHTTP(w, r)
  9. })
  10. }
  11. mux.Handle("POST /posts", auth.HTTPMiddleware(requireScope("write", createPost)))

createPost 為示意的應用程式 handler。
EN