v1.0.3

Middleware

Last updated


go-jwt ships Gin and net/http middleware; both verify the request with Verify() and hand *Auth to the next handler on success.

Behavior


ItemGinMiddleware()HTTPMiddleware(next)
VerificationVerify(c.Writer, c.Request)Verify(w, r)
Failure responsec.JSON(StatusCode, {"error": ...}) then c.Abort()Content-Type: application/json, StatusCode, {"error": ...}; next is not called
Stored on successc.Set("user", *Auth)context.WithValue(r.Context(), "user", *Auth)
Read backGetAuthDataFromGinContext(c)GetAuthDataFromHTTPRequest(r)

The failure response carries only error, not ErrorTag; to branch on the error type, call Verify() directly in your own handler.
Refresh happens inside the middleware; new tokens leave with the downstream handler's response via Set-Cookie (plus X-New-Access-Token on re-sign only).

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("failed to init 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 uses the plain string "user" as the context key. Anything else in the application that stores a value under the string "user" collides with it; always read through GetAuthDataFromHTTPRequest(), which returns false when the value is not *Auth.

Authorizing by Role or Scope


The middleware only authenticates. Auth.Role, Auth.Scope, and Auth.Level are checked by the application:
  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 stands for your application's handler.
中文