Files
2026-09-11 01:41:52 +08:00

123 lines
4.4 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package redisdao
import (
"context"
"fmt"
"time"
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/global"
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/redismodel"
"github.com/redis/go-redis/v9"
)
// authTokenSAddScript 向用户 token 集合加入 refresh_token,仅在集合由空变为非空时设置 TTL,
// 避免每次登录都刷新集合 TTL 导致旧 token 条目长期驻留。
const authTokenSAddScript = `
local added = redis.call('SADD', KEYS[1], ARGV[1])
if added == 1 and redis.call('SCARD', KEYS[1]) == 1 then
redis.call('EXPIRE', KEYS[1], ARGV[2])
end
return added
`
// AuthTokenSet 登录成功后写入会话:
// - access_token -> user_id
// - refresh_token -> user_id
// - user_id 的 token 集合加入 refresh_token(用于多端管理 / 登出)
func AuthTokenSet(ctx context.Context, userID int64, accessToken, refreshToken string) error {
ctx, span := global.Tracer.Start(ctx, "redisdao.AuthTokenSet")
defer span.End()
accessKey := fmt.Sprintf(redismodel.KeyAuthAccess, accessToken)
refreshKey := fmt.Sprintf(redismodel.KeyAuthRefresh, refreshToken)
userTokensKey := fmt.Sprintf(redismodel.KeyAuthUserTokens, userID)
accessTTL := time.Duration(global.JWTSetting.AccessTokenExpire) * time.Second
refreshTTL := time.Duration(global.JWTSetting.RefreshTokenExpire) * time.Second
pipe := global.RedisEngine.TxPipeline()
pipe.Set(ctx, accessKey, userID, accessTTL)
pipe.Set(ctx, refreshKey, userID, refreshTTL)
pipe.Eval(ctx, authTokenSAddScript, []string{userTokensKey}, refreshToken, int(refreshTTL.Seconds()))
if _, err := pipe.Exec(ctx); err != nil {
span.RecordError(err)
return err
}
return nil
}
// AuthTokenDel 登出/失效:删除 access、refresh 映射,并从用户 token 集合移除 refresh_token
func AuthTokenDel(ctx context.Context, userID int64, accessToken, refreshToken string) error {
ctx, span := global.Tracer.Start(ctx, "redisdao.AuthTokenDel")
defer span.End()
accessKey := fmt.Sprintf(redismodel.KeyAuthAccess, accessToken)
refreshKey := fmt.Sprintf(redismodel.KeyAuthRefresh, refreshToken)
userTokensKey := fmt.Sprintf(redismodel.KeyAuthUserTokens, userID)
pipe := global.RedisEngine.TxPipeline()
pipe.Del(ctx, accessKey)
pipe.Del(ctx, refreshKey)
pipe.SRem(ctx, userTokensKey, refreshToken)
if _, err := pipe.Exec(ctx); err != nil {
span.RecordError(err)
return err
}
return nil
}
// AuthUserIDByAccess 通过 access token 查询 user_id,token 不存在或已过期返回 redis.Nil
func AuthUserIDByAccess(ctx context.Context, accessToken string) (int64, error) {
ctx, span := global.Tracer.Start(ctx, "redisdao.AuthUserIDByAccess")
defer span.End()
key := fmt.Sprintf(redismodel.KeyAuthAccess, accessToken)
userID, err := global.RedisEngine.Get(ctx, key).Int64()
if err != nil && err != redis.Nil {
span.RecordError(err)
}
return userID, err
}
// AuthUserIDByRefresh 通过 refresh token 查询 user_id,token 不存在或已过期返回 redis.Nil
func AuthUserIDByRefresh(ctx context.Context, refreshToken string) (int64, error) {
ctx, span := global.Tracer.Start(ctx, "redisdao.AuthUserIDByRefresh")
defer span.End()
key := fmt.Sprintf(redismodel.KeyAuthRefresh, refreshToken)
userID, err := global.RedisEngine.Get(ctx, key).Int64()
if err != nil && err != redis.Nil {
span.RecordError(err)
}
return userID, err
}
// sessionKeyTTL session_key 缓存时长(30 天,与微信 session_key 有效期对齐)。
const sessionKeyTTL = 30 * 24 * time.Hour
// AuthSessionKeySet 写入用户的 session_key(登录时 code2session 返回,用于虚拟支付 signature 计算)。
func AuthSessionKeySet(ctx context.Context, userID int64, sessionKey string) error {
ctx, span := global.Tracer.Start(ctx, "redisdao.AuthSessionKeySet")
defer span.End()
key := fmt.Sprintf(redismodel.KeyAuthSessionKey, userID)
if err := global.RedisEngine.Set(ctx, key, sessionKey, sessionKeyTTL).Err(); err != nil {
span.RecordError(err)
return err
}
return nil
}
// AuthSessionKeyGet 读取用户的 session_key,不存在返回 redis.Nil。
func AuthSessionKeyGet(ctx context.Context, userID int64) (string, error) {
ctx, span := global.Tracer.Start(ctx, "redisdao.AuthSessionKeyGet")
defer span.End()
key := fmt.Sprintf(redismodel.KeyAuthSessionKey, userID)
sessionKey, err := global.RedisEngine.Get(ctx, key).Result()
if err != nil && err != redis.Nil {
span.RecordError(err)
}
return sessionKey, err
}