From 5b01c9ada88218bbc0c27d56d79db173b6142cfd Mon Sep 17 00:00:00 2001 From: zhangwenjian Date: Fri, 4 Sep 2026 21:13:50 +0800 Subject: [PATCH] =?UTF-8?q?fix=F0=9F=90=9B:=20build=20the=20shared=20jwt?= =?UTF-8?q?=20middleware=20instance=20once=20in=20InitMiddleware?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four modules each called AuthInit and built their own instance, so which one Runtime handed back was decided by whichever module initialised last. The JwtToken key was also registered as an unbound method expression, which GetHandlerFunc's type assertion can never match - the key was registered and unusable at the same time. The instance is now built once here and registered as a bound closure. Modules read it back through GetAuthMiddleware, which is fatal rather than nil when called before InitMiddleware has run: a process without a JWT middleware should not reach the point of serving a request. Only one call site needs the instance itself rather than the handler (admin's /login, for LoginHandler); the thirty-odd MiddlewareFunc() call sites are unchanged. Claude-Session: https://claude.ai/code/session_01HPTAw8b8tAdFNFn8rKdPYx --- app/admin/router/init_router.go | 8 +++----- app/demo/router/router.go | 8 +++----- app/jobs/router/init_router.go | 7 +++---- app/other/router/init_router.go | 7 +++---- common/middleware/auth.go | 31 ++++++++++++++++++++++++++++-- common/middleware/init.go | 34 ++++++++++++++++++++++++++++----- 6 files changed, 70 insertions(+), 25 deletions(-) diff --git a/app/admin/router/init_router.go b/app/admin/router/init_router.go index 991965b7..83c080bc 100644 --- a/app/admin/router/init_router.go +++ b/app/admin/router/init_router.go @@ -25,11 +25,9 @@ func InitRouter() { os.Exit(-1) } - // the jwt middleware - authMiddleware, err := common.AuthInit() - if err != nil { - log.Fatalf("JWT Init Error, %s", err.Error()) - } + // the jwt middleware: shared instance InitMiddleware built at startup, + // not one built here per module (see common/middleware.GetAuthMiddleware). + authMiddleware := common.GetAuthMiddleware() // 注册系统路由 InitSysRouter(r, authMiddleware) diff --git a/app/demo/router/router.go b/app/demo/router/router.go index d994ed46..e7ff76e1 100644 --- a/app/demo/router/router.go +++ b/app/demo/router/router.go @@ -33,11 +33,9 @@ func InitRouter() { os.Exit(-1) } - // the jwt middleware - authMiddleware, err := common.AuthInit() - if err != nil { - log.Fatalf("JWT Init Error, %s", err.Error()) - } + // the jwt middleware: shared instance InitMiddleware built at startup, + // not one built here per module (see common/middleware.GetAuthMiddleware). + authMiddleware := common.GetAuthMiddleware() // 注册业务路由 InitBusinessRouter(r, authMiddleware) diff --git a/app/jobs/router/init_router.go b/app/jobs/router/init_router.go index a0fe10cb..4f0521bd 100644 --- a/app/jobs/router/init_router.go +++ b/app/jobs/router/init_router.go @@ -26,10 +26,9 @@ func InitRouter() { os.Exit(-1) } - authMiddleware, err := common.AuthInit() - if err != nil { - log.Fatalf("JWT Init Error, %s", err.Error()) - } + // the jwt middleware: shared instance InitMiddleware built at startup, + // not one built here per module (see common/middleware.GetAuthMiddleware). + authMiddleware := common.GetAuthMiddleware() // 注册业务路由 initRouter(r, authMiddleware) diff --git a/app/other/router/init_router.go b/app/other/router/init_router.go index 9c75a1df..b25a297a 100644 --- a/app/other/router/init_router.go +++ b/app/other/router/init_router.go @@ -25,10 +25,9 @@ func InitRouter() { os.Exit(-1) } // the jwt middleware - authMiddleware, err := common.AuthInit() - if err != nil { - log.Fatalf("JWT Init Error, %s", err.Error()) - } + // the jwt middleware: shared instance InitMiddleware built at startup, + // not one built here per module (see common/middleware.GetAuthMiddleware). + authMiddleware := common.GetAuthMiddleware() // 注册业务路由 // TODO: 这里可存放业务路由,里边并无实际路由只有演示代码 diff --git a/common/middleware/auth.go b/common/middleware/auth.go index caeb9baa..08efba33 100644 --- a/common/middleware/auth.go +++ b/common/middleware/auth.go @@ -3,11 +3,19 @@ package middleware import ( "time" - "github.com/go-admin-team/go-admin-core/v2/sdk/config" jwt "github.com/go-admin-team/go-admin-core/v2/jwtauth" + log "github.com/go-admin-team/go-admin-core/v2/logger" + "github.com/go-admin-team/go-admin-core/v2/sdk/config" "go-admin/common/middleware/handler" ) +// authMiddleware is the single JWT middleware instance the whole process +// shares. InitMiddleware builds it once, before any module registers its +// routes; GetAuthMiddleware is how a module gets it back instead of calling +// AuthInit itself and building another, functionally-equivalent-but-distinct +// instance. +var authMiddleware *jwt.GinJWTMiddleware + // AuthInit jwt验证new func AuthInit() (*jwt.GinJWTMiddleware, error) { timeout := time.Hour @@ -33,4 +41,23 @@ func AuthInit() (*jwt.GinJWTMiddleware, error) { TimeFunc: time.Now, }) -} \ No newline at end of file +} + +// GetAuthMiddleware returns the shared JWT middleware instance InitMiddleware +// built at startup. Application modules (app/admin, app/jobs, app/other, +// app/demo) call this instead of AuthInit so their router chains - which +// still need the instance itself for authMiddleware.MiddlewareFunc() and +// authMiddleware.LoginHandler, not just the bound closure registered under +// sdk.Runtime's JwtTokenCheck key - end up using the same instance the host +// registered, rather than one each. +// +// It fails loudly instead of returning nil: an InitRouter that runs before +// InitMiddleware has a real startup-ordering bug, not a case to paper over +// with a nil *jwt.GinJWTMiddleware that would panic much further down the +// call chain with a far less useful stack trace. +func GetAuthMiddleware() *jwt.GinJWTMiddleware { + if authMiddleware == nil { + log.Fatal("JWT middleware not initialized; InitMiddleware must run before any module's InitRouter") + } + return authMiddleware +} diff --git a/common/middleware/init.go b/common/middleware/init.go index 96e93262..947c7e56 100644 --- a/common/middleware/init.go +++ b/common/middleware/init.go @@ -2,15 +2,20 @@ package middleware import ( "github.com/gin-gonic/gin" - jwt "github.com/go-admin-team/go-admin-core/v2/jwtauth" + log "github.com/go-admin-team/go-admin-core/v2/logger" "github.com/go-admin-team/go-admin-core/v2/sdk" + "github.com/go-admin-team/go-admin-core/v2/sdk/runtime" "go-admin/common/actions" ) +// These alias core's own constants (see sdk/runtime.GetHandlerFunc's contract +// doc, section 9) rather than redeclaring the same three strings, so a typo +// here can no longer split registration and lookup into two different keys +// that both happen to compile. const ( - JwtTokenCheck string = "JwtToken" - RoleCheck string = "AuthCheckRole" - PermissionCheck string = "PermissionAction" + JwtTokenCheck = runtime.JwtTokenCheck + RoleCheck = runtime.RoleCheck + PermissionCheck = runtime.PermissionCheck ) func InitMiddleware(r *gin.Engine) { @@ -29,7 +34,26 @@ func InitMiddleware(r *gin.Engine) { r.Use(Secure) // 链路追踪 //r.Use(middleware.Trace()) - sdk.Runtime.SetMiddleware(JwtTokenCheck, (*jwt.GinJWTMiddleware).MiddlewareFunc) + + // Build the shared JWT middleware instance here, before any module + // registers routes (initRouter runs ahead of runStartupHooks, which is + // what invokes each module's InitRouter - see cmd/api/server.go). Doing + // it once here, instead of once per module via AuthInit, is what makes + // GetAuthMiddleware and sdk.Runtime.GetHandlerFunc(JwtTokenCheck) both + // resolve to a single, meaningful instance instead of "whichever module + // happened to initialize last". + // + // SetMiddleware must be given a bound closure (authMiddleware.MiddlewareFunc()), + // not the unbound method expression (*jwt.GinJWTMiddleware).MiddlewareFunc: + // the latter has no receiver bound to it, so GetHandlerFunc's type + // assertion to gin.HandlerFunc always fails for it. + var err error + authMiddleware, err = AuthInit() + if err != nil { + // A process with no JWT middleware must not start serving requests. + log.Fatalf("JWT Init Error, %s", err.Error()) + } + sdk.Runtime.SetMiddleware(JwtTokenCheck, authMiddleware.MiddlewareFunc()) sdk.Runtime.SetMiddleware(RoleCheck, AuthCheckRole()) sdk.Runtime.SetMiddleware(PermissionCheck, actions.PermissionAction()) }