141 lines
5.2 KiB
Go
141 lines
5.2 KiB
Go
package token
|
||
|
||
import (
|
||
"catface/app/global/consts"
|
||
"catface/app/global/my_errors"
|
||
"catface/app/global/variable"
|
||
"catface/app/http/middleware/my_jwt"
|
||
"catface/app/model"
|
||
"catface/app/service/users/token_cache_redis"
|
||
"errors"
|
||
"time"
|
||
|
||
"github.com/dgrijalva/jwt-go"
|
||
)
|
||
|
||
// CreateUserFactory 创建 userToken 工厂
|
||
func CreateUserFactory() *userToken {
|
||
return &userToken{
|
||
userJwt: my_jwt.CreateMyJWT(variable.ConfigYml.GetString("Token.JwtTokenSignKey")),
|
||
}
|
||
}
|
||
|
||
type userToken struct {
|
||
userJwt *my_jwt.JwtSign
|
||
}
|
||
|
||
// GenerateToken 生成token
|
||
func (u *userToken) GenerateToken(userid int64, username string, phone string, expireAt int64) (tokens string, err error) {
|
||
|
||
// 根据实际业务自定义token需要包含的参数,生成token,注意:用户密码请勿包含在token
|
||
customClaims := my_jwt.CustomClaims{
|
||
UserId: userid,
|
||
Name: username,
|
||
Phone: phone,
|
||
// 特别注意,针对前文的匿名结构体,初始化的时候必须指定键名,并且不带 jwt. 否则报错:Mixture of field: value and value initializers
|
||
StandardClaims: jwt.StandardClaims{
|
||
NotBefore: time.Now().Unix() - 10, // 生效开始时间
|
||
ExpiresAt: time.Now().Unix() + expireAt, // 失效截止时间
|
||
},
|
||
}
|
||
return u.userJwt.CreateToken(customClaims)
|
||
}
|
||
|
||
// RecordLoginToken 用户login成功,记录用户token
|
||
func (u *userToken) RecordLoginToken(userToken, clientIp string) bool {
|
||
if customClaims, err := u.userJwt.ParseToken(userToken); err == nil {
|
||
userId := customClaims.UserId
|
||
expiresAt := customClaims.ExpiresAt
|
||
return model.CreateUserFactory("").OauthLoginToken(userId, userToken, expiresAt, clientIp)
|
||
} else {
|
||
return false
|
||
}
|
||
}
|
||
|
||
// TokenIsMeetRefreshCondition 检查token是否满足刷新条件
|
||
func (u *userToken) TokenIsMeetRefreshCondition(token string) bool {
|
||
// token基本信息是否有效:1.过期时间在允许的过期范围内;2.基本格式正确
|
||
customClaims, code := u.isNotExpired(token, variable.ConfigYml.GetInt64("Token.JwtTokenRefreshAllowSec"))
|
||
switch code {
|
||
case consts.JwtTokenOK, consts.JwtTokenExpired:
|
||
//在数据库的存储信息是否也符合过期刷新刷新条件
|
||
if model.CreateUserFactory("").OauthRefreshConditionCheck(customClaims.UserId, token) {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
// RefreshToken 刷新token的有效期(默认+3600秒,参见常量配置项)
|
||
func (u *userToken) RefreshToken(oldToken, clientIp string) (newToken string, res bool) {
|
||
var err error
|
||
//如果token是有效的、或者在过期时间内,那么执行更新,换取新token
|
||
if newToken, err = u.userJwt.RefreshToken(oldToken, variable.ConfigYml.GetInt64("Token.JwtTokenRefreshExpireAt")); err == nil {
|
||
if customClaims, err := u.userJwt.ParseToken(newToken); err == nil {
|
||
userId := customClaims.UserId
|
||
expiresAt := customClaims.ExpiresAt
|
||
if model.CreateUserFactory("").OauthRefreshToken(userId, expiresAt, oldToken, newToken, clientIp) {
|
||
return newToken, true
|
||
}
|
||
}
|
||
}
|
||
|
||
return "", false
|
||
}
|
||
|
||
// 判断token本身是否未过期
|
||
// 参数解释:
|
||
// token: 待处理的token值
|
||
// expireAtSec: 过期时间延长的秒数,主要用于用户刷新token时,判断是否在延长的时间范围内,非刷新逻辑默认为0
|
||
func (u *userToken) isNotExpired(token string, expireAtSec int64) (*my_jwt.CustomClaims, int) {
|
||
if customClaims, err := u.userJwt.ParseToken(token); err == nil {
|
||
|
||
if time.Now().Unix()-(customClaims.ExpiresAt+expireAtSec) < 0 {
|
||
// token有效
|
||
return customClaims, consts.JwtTokenOK
|
||
} else {
|
||
// 过期的token
|
||
return customClaims, consts.JwtTokenExpired
|
||
}
|
||
} else {
|
||
// 无效的token
|
||
return nil, consts.JwtTokenInvalid
|
||
}
|
||
}
|
||
|
||
// IsEffective 判断token是否有效(未过期+数据库用户信息正常)
|
||
func (u *userToken) IsEffective(token string) bool {
|
||
customClaims, code := u.isNotExpired(token, 0)
|
||
if consts.JwtTokenOK == code {
|
||
//1.首先在redis检测是否存在某个用户对应的有效token,如果存在就直接返回,不再继续查询mysql,否则最后查询mysql逻辑,确保万无一失
|
||
if variable.ConfigYml.GetInt("Token.IsCacheToRedis") == 1 {
|
||
tokenRedisFact := token_cache_redis.CreateUsersTokenCacheFactory(customClaims.UserId)
|
||
if tokenRedisFact != nil {
|
||
defer tokenRedisFact.ReleaseRedisConn()
|
||
if tokenRedisFact.TokenCacheIsExists(token) {
|
||
return true
|
||
}
|
||
}
|
||
}
|
||
//2.token符合token本身的规则以后,继续在数据库校验是不是符合本系统其他设置,例如:一个用户默认只允许10个账号同时在线(10个token同时有效)
|
||
if model.CreateUserFactory("").OauthCheckTokenIsOk(customClaims.UserId, token) {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
// ParseToken 将 token 解析为绑定时传递的参数
|
||
func (u *userToken) ParseToken(tokenStr string) (CustomClaims my_jwt.CustomClaims, err error) {
|
||
if customClaims, err := u.userJwt.ParseToken(tokenStr); err == nil {
|
||
return *customClaims, nil
|
||
} else {
|
||
return my_jwt.CustomClaims{}, errors.New(my_errors.ErrorsParseTokenFail)
|
||
}
|
||
}
|
||
|
||
// DestroyToken 销毁token,基本用不到,因为一个网站的用户退出都是直接关闭浏览器窗口,极少有户会点击“注销、退出”等按钮,销毁token其实无多大意义
|
||
func (u *userToken) DestroyToken() {
|
||
|
||
}
|