You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
FuShouXian-Backend/module/usercenter/model.user.token.go

228 lines
5.1 KiB

package usercenter
import (
"context"
"errors"
"log"
"sync"
"time"
"github.com/towgo/towgo/dao/basedboperat"
"github.com/towgo/towgo/lib/system"
)
// 缓存有效期
var memCacheTimer int64 = 60 * 10
// token有效期 秒单位计算
var expirationLimit int64 = 86400 * 20
var autoClearLimit int64 = 60 * 10 //10分钟清理一次过期的token
// var expirationLimit int64 = 60
var memCache *MemCache
// UserToken结构体
func (UserToken) TableName() string {
return _tableHead + "users_token"
}
type UserToken struct {
TokenKey string
Uid int64
Payload any `gorm:"-" xorm:"-"`
Expiration int64
UpdatedAt int64
CreatedAt int64
}
func InitTokenTask() {
memCache = &MemCache{}
autoTimeToClear()
}
type MemCache struct {
timers sync.Map
cacheObject sync.Map
cancels sync.Map
}
func (mc *MemCache) CreateTimerToDelete(tokenKey string) {
timer := time.NewTimer(time.Second * time.Duration(memCacheTimer))
mc.timers.Store(tokenKey, timer)
ctx, cancel := context.WithCancel(context.Background())
mc.cancels.Store(tokenKey, cancel)
go mc.DeleteWhenTimeOut(ctx, tokenKey, timer)
}
func (mc *MemCache) DeleteWhenTimeOut(ctx context.Context, tokenKey string, timer *time.Timer) {
select {
case <-timer.C:
mc.timers.Delete(tokenKey)
mc.cacheObject.Delete(tokenKey)
mc.cancels.Delete(tokenKey)
return
case <-ctx.Done():
return
}
}
func (mc *MemCache) ResetTimer(tokenKey string) {
timerInterface, ok := mc.timers.Load(tokenKey)
if ok {
timer := timerInterface.(*time.Timer)
timer.Reset(time.Second * time.Duration(memCacheTimer))
}
}
func (mc *MemCache) Add(tokenKey string, value any) {
mc.cacheObject.Store(tokenKey, value)
mc.CreateTimerToDelete(tokenKey)
}
func (mc *MemCache) Del(tokenKey string) {
timerInterface, ok := mc.timers.Load(tokenKey)
if ok {
timer := timerInterface.(*time.Timer)
timer.Stop() //关闭定时器
cancel_any, isLoaded := mc.cancels.LoadAndDelete(tokenKey) //关闭定时器线程
if isLoaded {
cancel := cancel_any.(context.CancelFunc)
if cancel != nil {
cancel()
}
}
mc.timers.Delete(tokenKey) //清除定时器委托
}
mc.cacheObject.Delete(tokenKey) //清除内存
}
func (mc *MemCache) Get(tokenKey string) (any, bool) {
return mc.cacheObject.Load(tokenKey)
}
// 自动清理过期token定时器
func autoTimeToClear() {
go func() {
defer func() {
err := recover()
if err != nil {
log.Print(err)
}
autoTimeToClear()
}()
var userToken UserToken
for {
time.Sleep(time.Second * time.Duration(autoClearLimit))
basedboperat.SqlExec("delete from "+userToken.TableName()+" where expiration < ?", time.Now().Unix())
}
}()
}
func (t *UserToken) NewTokenGUID(salt string) {
guid := system.GetGUID().Hex()
saltEncode := system.MD5(salt)
tokenCode := system.MD5(guid + saltEncode)
t.TokenKey = tokenCode
}
// 返回一个唯一标识的token令牌
func (t *UserToken) String() string {
return t.TokenKey
}
// token是否有效 检查有效期
// 有效返回true
// 无效返回false
func (t *UserToken) Valid() bool {
return time.Now().Unix() < t.Expiration
}
func (t *UserToken) Check(tokenKey string) bool {
return tokenKey == t.TokenKey
}
// 更新token有效期
func (t *UserToken) Update(expiration int64) {
if expiration > 0 {
t.Expiration = time.Now().Unix() + expiration
} else {
t.Expiration = time.Now().Unix() + expirationLimit
}
}
// 新建一个token
func NewToken(user *User) *UserToken {
timenow := time.Now().Unix()
token := &UserToken{
CreatedAt: timenow,
Uid: user.ID,
Payload: user,
Expiration: timenow + expirationLimit,
}
user.Token = token.TokenKey
token.NewTokenGUID(user.Salt)
basedboperat.Create(token)
memCache.Add(token.TokenKey, token)
return token
}
func GetToken(tokenKey string) (*UserToken, error) {
var userToken *UserToken = &UserToken{}
//缓存查询
userTokenInterface, ok := memCache.Get(tokenKey)
if ok {
//缓存命中
userToken = userTokenInterface.(*UserToken)
//缓存过期 清理
if !userToken.Valid() {
memCache.Del(tokenKey)
return nil, errors.New("token过期(登录失效,请重新登录)")
}
} else {
//数据库查询
//查询持久化数据
err := basedboperat.Get(userToken, nil, "token_key = ?", tokenKey)
if err != nil {
return nil, err
}
if userToken.Uid == 0 {
return nil, errors.New("token不存在(登录失效,请重新登录)") //数据不存在
}
//token过期
if !userToken.Valid() {
return nil, errors.New("token过期(登录失效,请重新登录)")
}
//查询token关联的用户
var user *User = &User{}
basedboperat.Get(user, nil, "id = ?", userToken.Uid)
if user.ID == 0 {
return nil, errors.New("token关联用户不存在(登录失效,请重新登录)") //用户不存在
}
//写入缓存
userToken.Payload = user
memCache.Add(userToken.TokenKey, userToken)
}
return userToken, nil
}
func DeleteToken(tokenKey string) {
memCache.Del(tokenKey)
var userToken *UserToken = &UserToken{}
userToken.TokenKey = tokenKey
basedboperat.Delete(userToken, nil, "token_key = ?", tokenKey)
}
func SetExpiration(hour int64) {
expirationLimit = 3600 * hour
}