This is an automated email from the ASF dual-hosted git repository.

hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-gateway.git


The following commit(s) were added to refs/heads/master by this push:
     new 8a5505b  fix(session): stop minting a session id for every request
8a5505b is described below

commit 8a5505b12d6ee59a3c623cfe6f30bf796a51af6d
Author: Yang Luo <[email protected]>
AuthorDate: Fri Sep 4 09:33:48 2026 +0800

    fix(session): stop minting a session id for every request
---
 conf/app.conf          |  1 -
 controllers/base.go    | 26 ++++++++++---
 controllers/session.go | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++
 main.go                | 15 +-------
 4 files changed, 122 insertions(+), 19 deletions(-)

diff --git a/conf/app.conf b/conf/app.conf
index 45cd6e6..b56e46b 100644
--- a/conf/app.conf
+++ b/conf/app.conf
@@ -18,7 +18,6 @@ httpport = 17000
 ; inside such a sandbox is the sandbox rather than this machine.
 httpaddr = 127.0.0.1
 runmode = dev
-SessionOn = true
 copyrequestbody = true
 
 ; Names Gateway answers to, beside this host's own addresses, "localhost" and
diff --git a/controllers/base.go b/controllers/base.go
index 660615c..98cb03a 100644
--- a/controllers/base.go
+++ b/controllers/base.go
@@ -48,22 +48,38 @@ func wrapActionResponse(affected bool, e ...error) 
*Response {
 }
 
 func (c *ApiController) GetSessionClaims() *auth.Claims {
-       s := c.GetSession("user")
+       store := c.sessionStore(false)
+       if store == nil {
+               return nil
+       }
+
+       s := store.Get("user")
        if s == nil {
                return nil
        }
 
-       claims := s.(auth.Claims)
+       claims, ok := s.(auth.Claims)
+       if !ok {
+               return nil
+       }
+
        return &claims
 }
 
 func (c *ApiController) SetSessionClaims(claims *auth.Claims) {
-       if claims == nil {
-               c.DelSession("user")
+       store := c.sessionStore(claims != nil)
+       if store == nil {
                return
        }
 
-       c.SetSession("user", *claims)
+       if claims == nil {
+               store.Delete("user")
+       } else {
+               store.Set("user", *claims)
+       }
+
+       // Nothing else writes the session out now that beego does not start it.
+       store.SessionRelease(c.Ctx.ResponseWriter)
 }
 
 func (c *ApiController) GetSessionUser() *auth.User {
diff --git a/controllers/session.go b/controllers/session.go
new file mode 100644
index 0000000..be2c021
--- /dev/null
+++ b/controllers/session.go
@@ -0,0 +1,99 @@
+// Copyright 2026 The casbin Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package controllers
+
+import (
+       "net/http"
+       "net/url"
+
+       "github.com/beego/beego"
+       "github.com/beego/beego/session"
+       _ "github.com/beego/beego/session/redis"
+)
+
+// InitSessions takes the session away from beego and gives it to the
+// controller. With SessionOn, beego starts one for every request that reaches
+// the router, minting a new id for each request that arrives without a cookie.
+// A page load makes several of those at once, so the browser is handed several
+// ids and keeps whichever response lands last — discarding the session the
+// sign-in on another of them had just written, which is why a freshly opened 
UI
+// reported "please sign in first" until it was reloaded.
+func InitSessions() {
+       beego.BConfig.WebConfig.Session.SessionOn = false
+
+       config := &session.ManagerConfig{
+               CookieName:      beego.BConfig.WebConfig.Session.SessionName,
+               EnableSetCookie: true,
+               Gclifetime:      3600 * 24 * 365,
+               Secure:          beego.BConfig.Listen.EnableHTTPS,
+               // The session is only ever presented by Gateway's own pages, 
so the
+               // browser is told to keep it off requests another site makes.
+               CookieSameSite: http.SameSiteStrictMode,
+               // Sessions are kept in files beside the binary, or in Redis 
when one is
+               // configured.
+               ProviderConfig: "./tmp",
+       }
+       provider := "file"
+       if redisEndpoint := beego.AppConfig.String("redisEndpoint"); 
redisEndpoint != "" {
+               provider = "redis"
+               config.ProviderConfig = redisEndpoint
+       }
+
+       sessions, err := session.NewManager(provider, config)
+       if err != nil {
+               panic(err)
+       }
+
+       beego.GlobalSessions = sessions
+       go sessions.GC()
+}
+
+// sessionStore is this request's session, or nil when it has none: it is read
+// only when the request carries one, and created only when something is about
+// to be stored in it.
+func (c *ApiController) sessionStore(create bool) session.Store {
+       if c.CruSession != nil {
+               return c.CruSession
+       }
+       if !create && !c.hasSession() {
+               return nil
+       }
+
+       store, err := beego.GlobalSessions.SessionStart(c.Ctx.ResponseWriter, 
c.Ctx.Request)
+       if err != nil {
+               beego.Error("the session could not be started:", err)
+               return nil
+       }
+
+       c.CruSession = store
+       return store
+}
+
+// hasSession reports whether the request presents a session that still exists,
+// so that a cookie left over from a session that is gone is not answered with 
a
+// new one.
+func (c *ApiController) hasSession() bool {
+       cookie, err := 
c.Ctx.Request.Cookie(beego.BConfig.WebConfig.Session.SessionName)
+       if err != nil || cookie.Value == "" {
+               return false
+       }
+
+       sid, err := url.QueryUnescape(cookie.Value)
+       if err != nil {
+               return false
+       }
+
+       return beego.GlobalSessions.GetProvider().SessionExist(sid)
+}
diff --git a/main.go b/main.go
index 6940d22..e7802c0 100644
--- a/main.go
+++ b/main.go
@@ -17,7 +17,6 @@ package main
 import (
        "errors"
        "fmt"
-       "net/http"
        "os"
        "time"
 
@@ -27,6 +26,7 @@ import (
        "github.com/apache/casbin-gateway/agentpatch"
        "github.com/apache/casbin-gateway/casdoor"
        "github.com/apache/casbin-gateway/conf"
+       "github.com/apache/casbin-gateway/controllers"
        "github.com/apache/casbin-gateway/mcpserver"
        "github.com/apache/casbin-gateway/object"
        "github.com/apache/casbin-gateway/proxy"
@@ -35,7 +35,6 @@ import (
        "github.com/apache/casbin-gateway/util"
        "github.com/apache/casbin-gateway/version"
        "github.com/beego/beego"
-       _ "github.com/beego/beego/session/redis"
 )
 
 // daemonLogPath is where a background Gateway sends the console output nobody
@@ -139,17 +138,7 @@ func main() {
        beego.InsertFilter("/*", beego.BeforeRouter, routers.TransparentStatic)
        beego.InsertFilter("/api/*", beego.BeforeRouter, routers.ApiFilter)
 
-       if beego.AppConfig.String("redisEndpoint") == "" {
-               beego.BConfig.WebConfig.Session.SessionProvider = "file"
-               beego.BConfig.WebConfig.Session.SessionProviderConfig = "./tmp"
-       } else {
-               beego.BConfig.WebConfig.Session.SessionProvider = "redis"
-               beego.BConfig.WebConfig.Session.SessionProviderConfig = 
beego.AppConfig.String("redisEndpoint")
-       }
-       beego.BConfig.WebConfig.Session.SessionGCMaxLifetime = 3600 * 24 * 365
-       // The session is only ever presented by Gateway's own pages, so the 
browser
-       // is told to keep it off requests another site makes.
-       beego.BConfig.WebConfig.Session.SessionCookieSameSite = 
http.SameSiteStrictMode
+       controllers.InitSessions()
 
        port := conf.GetHttpPort()
        addr := conf.GetHttpAddr()


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to