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]