业务场景:已经有一套在运行的php项目,但是性能和灵活性不足。需要开发部署一套go语言服务,但是要能兼容前项目的鉴权规则。那么我们除了将php的鉴权模块独立出来,放到网关,再就是在go服务里写一个同逻辑的中间件。

我发现了项目中这张表里存了用户的token和创建销毁时间,也就不难猜出,原来的后端是通过这里判断token有效和时间的。
那么作为后来者,我也可以通过这张表去对比前端携带的token是否合法,就有了以下的代码。
Go
// Token验证中间件(仅通过token验证)
func TokenAuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 从请求头获取token
token := c.GetHeader("Authorization")
if token == "" {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"error": "未授权",
})
c.Abort()
return
}
// 去除可能的"Bearer "前缀
if strings.HasPrefix(token, "Bearer ") {
token = strings.TrimPrefix(token, "Bearer ")
}
log.Printf("前端token%s:", token)
// 验证token有效性
isValid := verifyToken(token)
if !isValid {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"error": "未授权",
})
c.Abort()
return
}
// token验证通过,继续执行后续逻辑
c.Next()
}
}
// 通过token验证有效性
func verifyToken(token string) bool {
// 查询token对应的记录
type UserToken struct {
Createtime int64 `json:"createtime"`
Expiretime int64 `json:"expiretime"`
}
var userToken UserToken
// 查询该token对应的记录
query := `
SELECT createtime, expiretime
FROM fa_user_token
WHERE token = ?
LIMIT 1
`
err := dbManager.GetDB().QueryRow(query, token).Scan(
&userToken.Createtime,
&userToken.Expiretime,
)
if err != nil {
if err == sql.ErrNoRows {
log.Printf("token不存在: %s", token)
} else {
log.Printf("查询token失败: %s, 错误: %v", token, err)
}
return false
}
// 获取当前时间戳(秒级)
currentTime := time.Now().Unix()
// 验证是否过期
isExpired := false
// 1. 验证是否超过两个月(5184000秒 = 60天)
twoMonthsAgo := currentTime - 5184000
//2592000 一个月
//1296000 半个月
if userToken.Createtime < twoMonthsAgo {
isExpired = true
log.Printf("token已超过两个月: %s", token)
}
// 2. 检查是否已主动退出/销毁(expiretime字段有值且小于当前时间)
if userToken.Expiretime > 0 {
if currentTime > userToken.Expiretime {
isExpired = true
log.Printf("token已主动退出/销毁: %s, 销毁时间: %d", token, userToken.Expiretime)
} else {
log.Printf("token有效期内: %s, 过期时间: %d", token, userToken.Expiretime)
}
} else {
log.Printf("token无过期时间设置: %s", token)
}
if isExpired {
return false
}
log.Printf("token验证成功: %s", token)
return true
}
使用时作为中间件放入到函数中当做参数传入就ok了。
