2026-05-31 18:33:54 +08:00
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"apigo.cc/go/cast"
|
|
|
|
|
"apigo.cc/go/log"
|
|
|
|
|
"apigo.cc/go/redis"
|
|
|
|
|
"errors"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Session 会话对象
|
|
|
|
|
type Session struct {
|
|
|
|
|
id string
|
|
|
|
|
conn *redis.Redis
|
|
|
|
|
data map[string]any
|
|
|
|
|
funcAuthCache map[string]bool
|
|
|
|
|
lock sync.RWMutex
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
memorySessionData = map[string]map[string]any{}
|
|
|
|
|
memorySessionDataLock = sync.RWMutex{}
|
|
|
|
|
lastSessionClearTime int64
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// NewSession 创建或加载会话
|
|
|
|
|
func NewSession(id string, logger *log.Logger) *Session {
|
|
|
|
|
data := map[string]any{}
|
|
|
|
|
var conn *redis.Redis
|
|
|
|
|
|
|
|
|
|
timeout := Config.SessionTimeout
|
|
|
|
|
if timeout <= 0 {
|
|
|
|
|
timeout = 3600
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if Config.SessionRedis != "" {
|
|
|
|
|
conn = redis.GetRedis(Config.SessionRedis, logger)
|
|
|
|
|
err := conn.GET("SESS_" + id).To(&data)
|
|
|
|
|
if err == nil {
|
|
|
|
|
_ = conn.EXPIRE("SESS_"+id, timeout)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
memorySessionDataLock.RLock()
|
|
|
|
|
if d, ok := memorySessionData[id]; ok && d != nil {
|
|
|
|
|
for k, v := range d {
|
|
|
|
|
data[k] = v
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
memorySessionDataLock.RUnlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &Session{
|
|
|
|
|
id: id,
|
|
|
|
|
conn: conn,
|
|
|
|
|
data: data,
|
|
|
|
|
funcAuthCache: map[string]bool{},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set 设置会话数据
|
|
|
|
|
func (s *Session) Set(key string, value any) {
|
|
|
|
|
s.lock.Lock()
|
|
|
|
|
defer s.lock.Unlock()
|
|
|
|
|
s.data[key] = value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get 获取会话数据
|
|
|
|
|
func (s *Session) Get(key string) any {
|
|
|
|
|
s.lock.RLock()
|
|
|
|
|
defer s.lock.RUnlock()
|
|
|
|
|
return s.data[key]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove 移除会话数据
|
|
|
|
|
func (s *Session) Remove(key string) {
|
|
|
|
|
s.lock.Lock()
|
|
|
|
|
defer s.lock.Unlock()
|
|
|
|
|
delete(s.data, key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetAuthLevel 设置鉴权级别
|
|
|
|
|
func (s *Session) SetAuthLevel(level int) {
|
|
|
|
|
s.Set("_authLevel", level)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetAuthLevel 获取当前鉴权级别
|
|
|
|
|
func (s *Session) GetAuthLevel() int {
|
|
|
|
|
return cast.Int(s.Get("_authLevel"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save 保存会话数据
|
|
|
|
|
func (s *Session) Save() error {
|
|
|
|
|
s.lock.Lock()
|
|
|
|
|
defer s.lock.Unlock()
|
|
|
|
|
|
|
|
|
|
timeout := Config.SessionTimeout
|
|
|
|
|
if timeout <= 0 {
|
|
|
|
|
timeout = 3600
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if s.conn == nil {
|
|
|
|
|
now := time.Now().Unix()
|
|
|
|
|
s.data["_time"] = now
|
2026-06-04 18:16:46 +08:00
|
|
|
|
2026-05-31 18:33:54 +08:00
|
|
|
// 复制一份数据存储,防止外部修改
|
|
|
|
|
saveData := make(map[string]any)
|
|
|
|
|
for k, v := range s.data {
|
|
|
|
|
saveData[k] = v
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memorySessionDataLock.Lock()
|
|
|
|
|
memorySessionData[s.id] = saveData
|
2026-06-04 18:16:46 +08:00
|
|
|
|
2026-05-31 18:33:54 +08:00
|
|
|
clearTimeDiff := now - lastSessionClearTime
|
|
|
|
|
if clearTimeDiff > 60 {
|
|
|
|
|
lastSessionClearTime = now
|
|
|
|
|
}
|
|
|
|
|
memorySessionDataLock.Unlock()
|
|
|
|
|
|
|
|
|
|
if clearTimeDiff > 60 {
|
|
|
|
|
go clearMemorySession(int64(timeout))
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
} else {
|
|
|
|
|
if !s.conn.SETEX("SESS_"+s.id, timeout, s.data) {
|
|
|
|
|
return errors.New("redis save failed")
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func clearMemorySession(timeout int64) {
|
|
|
|
|
memorySessionDataLock.Lock()
|
|
|
|
|
defer memorySessionDataLock.Unlock()
|
|
|
|
|
now := time.Now().Unix()
|
|
|
|
|
for id, data := range memorySessionData {
|
|
|
|
|
if t, ok := data["_time"].(int64); ok {
|
|
|
|
|
if now-t > timeout {
|
|
|
|
|
delete(memorySessionData, id)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AuthFuncs 检查权限
|
|
|
|
|
func (s *Session) AuthFuncs(needFuncs ...string) bool {
|
|
|
|
|
if len(needFuncs) == 0 {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.lock.RLock()
|
|
|
|
|
cacheKey := strings.Join(needFuncs, "; ")
|
|
|
|
|
if res, ok := s.funcAuthCache[cacheKey]; ok {
|
|
|
|
|
s.lock.RUnlock()
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
s.lock.RUnlock()
|
|
|
|
|
|
|
|
|
|
userFuncs, _ := cast.ToSlice[string](s.Get("funcs"))
|
|
|
|
|
isOk := false
|
|
|
|
|
|
|
|
|
|
// 超级管理员判断
|
|
|
|
|
for _, uf := range userFuncs {
|
|
|
|
|
if uf == "system.superAdmin." || strings.HasPrefix(uf, "system.superAdmin.") {
|
|
|
|
|
isOk = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !isOk && len(userFuncs) > 0 {
|
|
|
|
|
requiredAuthTotal := 0
|
|
|
|
|
for _, nf := range needFuncs {
|
|
|
|
|
if strings.HasPrefix(nf, "&") {
|
|
|
|
|
requiredAuthTotal++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
normalAuthOk := 0
|
|
|
|
|
requiredAuthOk := 0
|
|
|
|
|
|
|
|
|
|
for _, nf := range needFuncs {
|
|
|
|
|
isRequired := false
|
|
|
|
|
matchFunc := nf
|
|
|
|
|
if strings.HasPrefix(nf, "&") {
|
|
|
|
|
isRequired = true
|
|
|
|
|
matchFunc = nf[1:]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, uf := range userFuncs {
|
|
|
|
|
if strings.HasPrefix(uf, matchFunc) {
|
|
|
|
|
if isRequired {
|
|
|
|
|
requiredAuthOk++
|
|
|
|
|
} else {
|
|
|
|
|
normalAuthOk++
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-06-04 18:16:46 +08:00
|
|
|
|
2026-05-31 18:33:54 +08:00
|
|
|
// 如果是非必需权限命中,或者必需权限已全部命中且至少命中了一个非必需权限(如果有)
|
|
|
|
|
if (normalAuthOk > 0 || requiredAuthTotal == len(needFuncs)) && requiredAuthOk == requiredAuthTotal {
|
|
|
|
|
isOk = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.lock.Lock()
|
|
|
|
|
s.funcAuthCache[cacheKey] = isOk
|
|
|
|
|
s.lock.Unlock()
|
|
|
|
|
return isOk
|
|
|
|
|
}
|