mirror of
https://github.com/click33/sa-token-go.git
synced 2025-12-24 13:48:04 +08:00
refactor: 更新刷新令牌处理并删除冗余令牌loginID映射
-删除了GenerateTokenAir和RefreshAccessToken方法中令牌loginID映射的保存。 -实现了将原始令牌存储值复制到新的访问令牌密钥,以维护JSON TokenInfo格式。
This commit is contained in:
@@ -110,12 +110,6 @@ func (rtm *RefreshTokenManager) GenerateTokenPair(loginID, device string, access
|
||||
}
|
||||
}
|
||||
|
||||
// Save token-loginID mapping (符合 Java sa-token 设计) | 保存 Token-LoginID 映射
|
||||
tokenKey := rtm.getTokenKey(accessToken)
|
||||
if err := rtm.storage.Set(tokenKey, loginID, rtm.accessTTL); err != nil {
|
||||
return nil, fmt.Errorf("failed to save token: %w", err)
|
||||
}
|
||||
|
||||
// Generate refresh token | 生成刷新令牌
|
||||
refreshTokenBytes := make([]byte, RefreshTokenLength)
|
||||
if _, err := rand.Read(refreshTokenBytes); err != nil {
|
||||
@@ -184,10 +178,14 @@ func (rtm *RefreshTokenManager) RefreshAccessToken(refreshToken string) (*Refres
|
||||
// Update access token info | 更新访问令牌信息
|
||||
oldInfo.AccessToken = newAccessToken
|
||||
|
||||
// Save token-loginID mapping (符合 Java sa-token 设计) | 保存 Token-LoginID 映射
|
||||
tokenKey := rtm.getTokenKey(newAccessToken)
|
||||
if err := rtm.storage.Set(tokenKey, oldInfo.LoginID, rtm.accessTTL); err != nil {
|
||||
return nil, fmt.Errorf("failed to save token: %w", err)
|
||||
// Copy original token storage value to new access token key, to keep JSON TokenInfo format
|
||||
// 复制原 access token 的存储值到新的 access token 键,保持 JSON TokenInfo 格式,避免破坏 IsLogin/CheckLogin
|
||||
oldTokenKey := rtm.getTokenKey(oldInfo.AccessToken)
|
||||
if data, err := rtm.storage.Get(oldTokenKey); err == nil && data != nil {
|
||||
newTokenKey := rtm.getTokenKey(newAccessToken)
|
||||
if err := rtm.storage.Set(newTokenKey, data, rtm.accessTTL); err != nil {
|
||||
return nil, fmt.Errorf("failed to save new access token: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Update storage | 更新存储
|
||||
|
||||
@@ -23,6 +23,7 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
||||
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/click33/sa-token-go/storage/memory v0.1.4/go.mod h1:nqyuEh23mNjcuG3aI/BqJFz71zkpsgjdStW1BC5lkB0=
|
||||
github.com/click33/sa-token-go/storage/memory v0.1.5/go.mod h1:HxN2NVLq7lx+sOmq5RmV0h8xJjEUJLm4Xt1Mq+9PV2s=
|
||||
github.com/click33/sa-token-go/storage/memory v0.1.6/go.mod h1:YNojcgyLC/uFrmReZLePCDQ5WK2fo2WWGRjRMvXVH74=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk=
|
||||
|
||||
131
stputil/stputil_test.go
Normal file
131
stputil/stputil_test.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package stputil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/click33/sa-token-go/core/config"
|
||||
"github.com/click33/sa-token-go/core/manager"
|
||||
"github.com/click33/sa-token-go/storage/memory"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// setupTestManager 初始化内存存储和全局 Manager
|
||||
func setupTestManager() {
|
||||
storage := memory.NewStorage()
|
||||
cfg := &config.Config{
|
||||
TokenName: "satoken",
|
||||
Timeout: 3600,
|
||||
IsConcurrent: true,
|
||||
IsShare: true,
|
||||
MaxLoginCount: -1,
|
||||
}
|
||||
mgr := manager.NewManager(storage, cfg)
|
||||
SetManager(mgr)
|
||||
}
|
||||
|
||||
func TestLoginAndIsLogin(t *testing.T) {
|
||||
setupTestManager()
|
||||
|
||||
token, err := Login("user1")
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, token)
|
||||
|
||||
assert.True(t, IsLogin(token))
|
||||
|
||||
loginID, err := GetLoginID(token)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "user1", loginID)
|
||||
}
|
||||
|
||||
func TestPermissionsHelpers(t *testing.T) {
|
||||
setupTestManager()
|
||||
|
||||
token, err := Login("user2")
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = SetPermissions("user2", []string{"user.read", "user.write"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// HasPermission / CheckPermission
|
||||
assert.True(t, HasPermission("user2", "user.read"))
|
||||
assert.NoError(t, CheckPermission(token, "user.read"))
|
||||
|
||||
// AND / OR helpers
|
||||
assert.True(t, HasPermissionsAnd("user2", []string{"user.read", "user.write"}))
|
||||
assert.True(t, HasPermissionsOr("user2", []string{"user.delete", "user.read"}))
|
||||
|
||||
// Permission list by token
|
||||
perms, err := GetPermissionList(token)
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, []string{"user.read", "user.write"}, perms)
|
||||
}
|
||||
|
||||
func TestRoleHelpers(t *testing.T) {
|
||||
setupTestManager()
|
||||
|
||||
token, err := Login("user3")
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = SetRoles("user3", []string{"Admin", "User"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// HasRole / CheckRole
|
||||
assert.True(t, HasRole("user3", "Admin"))
|
||||
assert.NoError(t, CheckRole(token, "Admin"))
|
||||
|
||||
// AND / OR helpers
|
||||
assert.True(t, HasRolesAnd("user3", []string{"Admin", "User"}))
|
||||
assert.True(t, HasRolesOr("user3", []string{"Guest", "Admin"}))
|
||||
|
||||
// Role list by token
|
||||
roles, err := GetRoleList(token)
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, []string{"Admin", "User"}, roles)
|
||||
}
|
||||
|
||||
func TestDisableAndCheckDisable(t *testing.T) {
|
||||
setupTestManager()
|
||||
|
||||
token, err := Login("user4")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 初始未封禁
|
||||
assert.NoError(t, CheckDisable(token))
|
||||
|
||||
// 封禁账号
|
||||
err = Disable("user4", time.Hour)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// 现在 CheckDisable 应返回错误(可能是“未登录”或“已封禁”等)
|
||||
err = CheckDisable(token)
|
||||
assert.Error(t, err)
|
||||
|
||||
disabled := IsDisable("user4")
|
||||
assert.True(t, disabled)
|
||||
}
|
||||
|
||||
func TestToStringHelpers(t *testing.T) {
|
||||
assert.Equal(t, "123", toString(123))
|
||||
assert.Equal(t, "-5", toString(int(-5)))
|
||||
assert.Equal(t, "0", toString(int64(0)))
|
||||
assert.Equal(t, "42", toString(uint(42)))
|
||||
assert.Equal(t, "", toString(struct{}{}))
|
||||
}
|
||||
|
||||
// TestLoginWithRefreshToken_IsLogin 验证双 Token 登录场景下,access token 能正常通过 IsLogin/CheckLogin
|
||||
func TestLoginWithRefreshToken_IsLogin(t *testing.T) {
|
||||
setupTestManager()
|
||||
|
||||
// 使用双 token 登录
|
||||
tokenInfo, err := LoginWithRefreshToken("user-refresh", "web")
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, tokenInfo.AccessToken)
|
||||
assert.NotEmpty(t, tokenInfo.RefreshToken)
|
||||
|
||||
// 刚登录的 access token 应该是“已登录”
|
||||
assert.True(t, IsLogin(tokenInfo.AccessToken))
|
||||
assert.NoError(t, CheckLogin(tokenInfo.AccessToken))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user