diff --git a/Sense/scripts/runtime/sense-common.ps1 b/Sense/scripts/runtime/sense-common.ps1 index e4cd351..03b32bc 100644 --- a/Sense/scripts/runtime/sense-common.ps1 +++ b/Sense/scripts/runtime/sense-common.ps1 @@ -228,7 +228,7 @@ function Initialize-SenseRuntime { ' enableddb: false', ' jwt:', " secret: $(ConvertTo-SenseYamlString $jwtSecret)", - ' timeout: 3600', + ' timeout: 2592000', ' database:', ' driver: postgres', " source: $(ConvertTo-SenseYamlString $databaseURL)", diff --git a/Sense/server/common/middleware/auth.go b/Sense/server/common/middleware/auth.go index 927743d..fdfbea6 100644 --- a/Sense/server/common/middleware/auth.go +++ b/Sense/server/common/middleware/auth.go @@ -8,16 +8,11 @@ import ( jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth" ) +const defaultLoginValidity = 30 * 24 * time.Hour + // AuthInit jwt验证new func AuthInit() (*jwt.GinJWTMiddleware, error) { - timeout := time.Hour - if config.ApplicationConfig.Mode == "dev" { - timeout = time.Duration(876010) * time.Hour - } else { - if config.JwtConfig.Timeout != 0 { - timeout = time.Duration(config.JwtConfig.Timeout) * time.Second - } - } + timeout := resolveJWTTimeout(config.ApplicationConfig.Mode, config.JwtConfig.Timeout) return jwt.New(&jwt.GinJWTMiddleware{ Realm: "Sense", Key: []byte(config.JwtConfig.Secret), @@ -34,3 +29,13 @@ func AuthInit() (*jwt.GinJWTMiddleware, error) { }) } + +func resolveJWTTimeout(mode string, configuredSeconds int64) time.Duration { + if mode == "dev" { + return time.Duration(876010) * time.Hour + } + if configuredSeconds > 0 { + return time.Duration(configuredSeconds) * time.Second + } + return defaultLoginValidity +} diff --git a/Sense/server/common/middleware/auth_test.go b/Sense/server/common/middleware/auth_test.go new file mode 100644 index 0000000..7a2e391 --- /dev/null +++ b/Sense/server/common/middleware/auth_test.go @@ -0,0 +1,26 @@ +package middleware + +import ( + "testing" + "time" +) + +func TestResolveJWTTimeoutDefaultsToThirtyDays(t *testing.T) { + for _, mode := range []string{"prod", "test", "demo"} { + if got := resolveJWTTimeout(mode, 0); got != 30*24*time.Hour { + t.Errorf("mode %s timeout=%s, want 720h", mode, got) + } + } +} + +func TestResolveJWTTimeoutHonorsExplicitConfiguration(t *testing.T) { + if got := resolveJWTTimeout("prod", 3600); got != time.Hour { + t.Fatalf("timeout=%s, want 1h", got) + } +} + +func TestResolveJWTTimeoutKeepsUpstreamDevelopmentBehavior(t *testing.T) { + if got := resolveJWTTimeout("dev", 1); got != 876010*time.Hour { + t.Fatalf("development timeout=%s", got) + } +} diff --git a/Sense/server/config/READMEN.md b/Sense/server/config/READMEN.md index 7923904..bd03ad6 100644 --- a/Sense/server/config/READMEN.md +++ b/Sense/server/config/READMEN.md @@ -20,7 +20,7 @@ settings: # JWT加密字符串 secret: "" # 过期时间单位:秒 - timeout: 3600 + timeout: 2592000 database: # 数据库名称 name: dbname diff --git a/Sense/server/config/settings.demo.yml b/Sense/server/config/settings.demo.yml index 2750d18..9cc760c 100644 --- a/Sense/server/config/settings.demo.yml +++ b/Sense/server/config/settings.demo.yml @@ -16,7 +16,7 @@ settings: frontpath: ../ui/src jwt: secret: "" - timeout: 3600 + timeout: 2592000 logger: # 日志存放路径 path: temp/logs diff --git a/Sense/server/config/settings.full.yml b/Sense/server/config/settings.full.yml index 4383d4f..3b5aa2b 100644 --- a/Sense/server/config/settings.full.yml +++ b/Sense/server/config/settings.full.yml @@ -34,7 +34,7 @@ settings: # token 密钥,生产环境时及的修改 secret: "" # token 过期时间 单位:秒 - timeout: 3600 + timeout: 2592000 database: # Sense 仅支持 PostgreSQL;连接信息由仓库外配置提供。 driver: postgres diff --git a/Sense/server/config/settings.sqlite.yml b/Sense/server/config/settings.sqlite.yml index 714c374..84187f1 100644 --- a/Sense/server/config/settings.sqlite.yml +++ b/Sense/server/config/settings.sqlite.yml @@ -25,7 +25,7 @@ settings: # token 密钥,生产环境时及的修改 secret: "" # token 过期时间 单位:秒 - timeout: 3600 + timeout: 2592000 database: # 文件名为上游兼容名称;Sense 仍只支持 PostgreSQL。 driver: postgres diff --git a/Sense/server/config/settings.yml b/Sense/server/config/settings.yml index b429001..e721a41 100644 --- a/Sense/server/config/settings.yml +++ b/Sense/server/config/settings.yml @@ -25,7 +25,7 @@ settings: # 必填。生产环境至少 32 个字符;不得提交真实值。 secret: "" # token 过期时间 单位:秒 - timeout: 3600 + timeout: 2592000 database: # 数据库类型 mysql, sqlite3, postgres, sqlserver # sqlserver: sqlserver://用户名:密码@地址?database=数据库名 diff --git a/Sense/tests/package/run-tests.ps1 b/Sense/tests/package/run-tests.ps1 index 3b2c269..814d07f 100644 --- a/Sense/tests/package/run-tests.ps1 +++ b/Sense/tests/package/run-tests.ps1 @@ -65,6 +65,7 @@ try { Assert-Equal 18071 $state.Port 'non-empty process environment must override sense.env' Assert-True (-not (Test-Path -LiteralPath $marker)) 'sense.env content must never execute' $generatedSettings = Get-Content -LiteralPath $state.SettingsPath -Raw + Assert-True $generatedSettings.Contains('timeout: 2592000') 'generated runtime settings must keep login valid for 30 days' Assert-True ((Get-SenseEnvironmentValue -Name 'SENSE_DATABASE_URL').Contains('p#&;=x')) 'special characters must survive env parsing' Assert-True ($generatedSettings.Contains('password=p#\u0026;=x')) 'special characters must be safely JSON-escaped in YAML' Assert-True (-not ((Get-SenseDatabaseInfo (Get-SenseEnvironmentValue -Name 'SENSE_DATABASE_URL')).Sanitized.Contains('password='))) 'PostgreSQL tool arguments must not contain password' diff --git a/Sense/ui/src/utils/auth.js b/Sense/ui/src/utils/auth.js index cbea20b..7ee3723 100644 --- a/Sense/ui/src/utils/auth.js +++ b/Sense/ui/src/utils/auth.js @@ -1,13 +1,14 @@ import Cookies from 'js-cookie' const TokenKey = 'Sense-Admin-Token' +const LoginValidityDays = 30 export function getToken() { return Cookies.get(TokenKey) } export function setToken(token) { - return Cookies.set(TokenKey, token) + return Cookies.set(TokenKey, token, { expires: LoginValidityDays }) } export function removeToken() { diff --git a/Sense/ui/tests/unit/utils/auth.spec.js b/Sense/ui/tests/unit/utils/auth.spec.js new file mode 100644 index 0000000..011106d --- /dev/null +++ b/Sense/ui/tests/unit/utils/auth.spec.js @@ -0,0 +1,32 @@ +import Cookies from 'js-cookie' +import { getToken, removeToken, setToken } from '@/utils/auth' + +jest.mock('js-cookie', () => ({ + get: jest.fn(), + set: jest.fn(), + remove: jest.fn() +})) + +describe('Sense login token cookie', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it('persists the token for 30 days', () => { + setToken('test-token') + + expect(Cookies.set).toHaveBeenCalledWith( + 'Sense-Admin-Token', + 'test-token', + { expires: 30 } + ) + }) + + it('reads and removes the same product-specific cookie', () => { + getToken() + removeToken() + + expect(Cookies.get).toHaveBeenCalledWith('Sense-Admin-Token') + expect(Cookies.remove).toHaveBeenCalledWith('Sense-Admin-Token') + }) +})