feat: 修改jwt逻辑,登出黑名单处理
This commit is contained in:
+13
-3
@@ -6,7 +6,7 @@ import (
|
|||||||
"sundynix-go/model/system"
|
"sundynix-go/model/system"
|
||||||
systemReq "sundynix-go/model/system/request"
|
systemReq "sundynix-go/model/system/request"
|
||||||
systemRes "sundynix-go/model/system/response"
|
systemRes "sundynix-go/model/system/response"
|
||||||
"sundynix-go/utils"
|
"sundynix-go/utils/jwt"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/mojocn/base64Captcha"
|
"github.com/mojocn/base64Captcha"
|
||||||
@@ -41,7 +41,17 @@ func (a *AuthApi) Login(c *gin.Context) {
|
|||||||
|
|
||||||
// 登出
|
// 登出
|
||||||
func (a *AuthApi) Logout(c *gin.Context) {
|
func (a *AuthApi) Logout(c *gin.Context) {
|
||||||
utils.ClearToken(c)
|
token := jwt.GetToken(c)
|
||||||
|
userId := jwt.GetUserId(c)
|
||||||
|
err := jwtService.PutBlacklist(userId, token)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("登出失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg("登出失败", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jwt.ClearToken(c)
|
||||||
|
response.OkWithMsg("登出成功", c)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Captcha api 生成验证码
|
// Captcha api 生成验证码
|
||||||
@@ -69,7 +79,7 @@ func (u *AuthApi) Captcha(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *AuthApi) GetToken(c *gin.Context, user system.User) {
|
func (a *AuthApi) GetToken(c *gin.Context, user system.User) {
|
||||||
token, claims, err := utils.GetLoginToken(&user)
|
token, claims, err := jwt.GetLoginToken(&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Error("GetToken err", zap.Error(err))
|
global.Logger.Error("GetToken err", zap.Error(err))
|
||||||
response.FailWithMsg("GetToken err", c)
|
response.FailWithMsg("GetToken err", c)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ type ApiGroup struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
jwtService = service.ServiceGroupApp.SystemServiceGroup.JwtService
|
||||||
userService = service.ServiceGroupApp.SystemServiceGroup.UserService
|
userService = service.ServiceGroupApp.SystemServiceGroup.UserService
|
||||||
clientService = service.ServiceGroupApp.SystemServiceGroup.ClientService
|
clientService = service.ServiceGroupApp.SystemServiceGroup.ClientService
|
||||||
roleService = service.ServiceGroupApp.SystemServiceGroup.RoleService
|
roleService = service.ServiceGroupApp.SystemServiceGroup.RoleService
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"sundynix-go/model/commom/response"
|
"sundynix-go/model/commom/response"
|
||||||
"sundynix-go/model/system"
|
"sundynix-go/model/system"
|
||||||
systemReq "sundynix-go/model/system/request"
|
systemReq "sundynix-go/model/system/request"
|
||||||
"sundynix-go/utils"
|
"sundynix-go/utils/jwt"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
@@ -89,7 +89,7 @@ func (m *MenuApi) GetUserMenuTree(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MenuApi) Route(c *gin.Context) {
|
func (m *MenuApi) Route(c *gin.Context) {
|
||||||
userId := utils.GetUserId(c)
|
userId := jwt.GetUserId(c)
|
||||||
routes, err := menuService.GetUserRoutes(userId)
|
routes, err := menuService.GetUserRoutes(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Error("获取用户菜单失败!", zap.Error(err))
|
global.Logger.Error("获取用户菜单失败!", zap.Error(err))
|
||||||
|
|||||||
+17
-8
@@ -2,34 +2,43 @@ package middleware
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"sundynix-go/model/commom/response"
|
"sundynix-go/model/commom/response"
|
||||||
"sundynix-go/utils"
|
"sundynix-go/service"
|
||||||
|
"sundynix-go/utils/jwt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var jwtService = service.ServiceGroupApp.SystemServiceGroup.JwtService
|
||||||
|
|
||||||
// AuthMiddleware 验证token有效性
|
// AuthMiddleware 验证token有效性
|
||||||
func AuthMiddleware() gin.HandlerFunc {
|
func AuthMiddleware() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
token := utils.GetToken(c)
|
token := jwt.GetToken(c)
|
||||||
if token == "" {
|
if token == "" {
|
||||||
response.NoAuth("未登录或非法访问", c)
|
response.NoAuth("未登录或非法访问", c)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//todo 黑名单处理
|
userId := jwt.GetUserId(c)
|
||||||
|
if jwtService.IsInBlacklist(userId, token) {
|
||||||
|
response.NoAuth("未登录或令牌失效", c)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
j := utils.NewJWT()
|
j := jwt.NewJWT()
|
||||||
// 解析token信息
|
// 解析token信息
|
||||||
claims, err := j.ParseToken(token)
|
claims, err := j.ParseToken(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, utils.TokenExpired) {
|
if errors.Is(err, jwt.TokenExpired) {
|
||||||
response.NoAuth("登录过期", c)
|
response.NoAuth("登录过期", c)
|
||||||
utils.ClearToken(c)
|
jwt.ClearToken(c)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.NoAuth(err.Error(), c)
|
response.NoAuth(err.Error(), c)
|
||||||
utils.ClearToken(c)
|
jwt.ClearToken(c)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,5 +13,6 @@ func (s *AuthRouter) InitAuthRouter(Router *gin.RouterGroup) {
|
|||||||
{
|
{
|
||||||
loginRouter.POST("login", authApi.Login)
|
loginRouter.POST("login", authApi.Login)
|
||||||
loginRouter.GET("captcha", authApi.Captcha)
|
loginRouter.GET("captcha", authApi.Captcha)
|
||||||
|
loginRouter.GET("logout", authApi.Logout) // 服务端不保存任何登录状态 退出实际是禁用了当前的jwt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package system
|
package system
|
||||||
|
|
||||||
type ServiceGroup struct {
|
type ServiceGroup struct {
|
||||||
|
JwtService
|
||||||
UserService
|
UserService
|
||||||
ClientService
|
ClientService
|
||||||
RoleService
|
RoleService
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JwtService struct{}
|
||||||
|
|
||||||
|
var JwtServiceApp = new(JwtService)
|
||||||
|
|
||||||
|
// 登出,禁用jwt
|
||||||
|
func (s JwtService) PutBlacklist(userId string, token string) (err error) {
|
||||||
|
expire, err := utils.ParseDuration(global.Config.JWT.ExpiresTime)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = global.Redis.Set(context.Background(), userId, token, expire).Err()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s JwtService) IsInBlacklist(userId string, token string) bool {
|
||||||
|
val, err := global.Redis.Get(context.Background(), userId).Result()
|
||||||
|
return err == nil && val == token
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
package utils
|
package jwt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
"sundynix-go/global"
|
"sundynix-go/global"
|
||||||
"sundynix-go/model/system"
|
"sundynix-go/model/system"
|
||||||
systemReq "sundynix-go/model/system/request"
|
systemReq "sundynix-go/model/system/request"
|
||||||
@@ -20,33 +21,7 @@ func GetLoginToken(user system.Login) (token string, claims systemReq.CustomClai
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetToken 设置一个名为 "x-token" 的 Cookie,并根据请求的主机名和 IP 地址来设置 Cookie 的域。
|
// GetToken 从请求头中获取JWT token,并确保其有效性
|
||||||
//
|
|
||||||
// 参数:
|
|
||||||
// - c: *gin.Context, Gin 框架的上下文对象,用于处理 HTTP 请求和响应。
|
|
||||||
// - token: string, 要设置的 token 值。
|
|
||||||
// - maxAge: int, Cookie 的最大生存时间(以秒为单位)。
|
|
||||||
//
|
|
||||||
// 该函数首先从请求的主机名中提取出主机部分(去除端口号),然后判断该主机名是否为 IP 地址。
|
|
||||||
// 如果是 IP 地址,则设置 Cookie 的域为空;否则,将 Cookie 的域设置为提取出的主机名。
|
|
||||||
func SetToken(c *gin.Context, token string, maxAge int) {
|
|
||||||
// 从请求的主机名中提取主机部分,忽略端口号
|
|
||||||
host, _, err := net.SplitHostPort(c.Request.Host)
|
|
||||||
if err != nil {
|
|
||||||
host = c.Request.Host
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断主机名是否为 IP 地址,并根据结果设置 Cookie 的域
|
|
||||||
if net.ParseIP(host) != nil {
|
|
||||||
c.SetCookie("x-token", token, maxAge, "/", "", false, false)
|
|
||||||
} else {
|
|
||||||
c.SetCookie("x-token", token, maxAge, "/", host, false, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetToken 从请求头或Cookie中获取JWT token,并确保其有效性。
|
|
||||||
// 如果请求头中没有Authorization字段,则尝试从Cookie中获取token,并解析验证其有效性。
|
|
||||||
// 如果token有效,则将其重新写入Cookie,并设置过期时间。
|
|
||||||
//
|
//
|
||||||
// 参数:
|
// 参数:
|
||||||
// - c: *gin.Context, Gin框架的上下文对象,用于获取请求信息和设置响应。
|
// - c: *gin.Context, Gin框架的上下文对象,用于获取请求信息和设置响应。
|
||||||
@@ -56,24 +31,10 @@ func SetToken(c *gin.Context, token string, maxAge int) {
|
|||||||
func GetToken(c *gin.Context) string {
|
func GetToken(c *gin.Context) string {
|
||||||
// 从请求头中获取Authorization字段的值
|
// 从请求头中获取Authorization字段的值
|
||||||
token := c.Request.Header.Get("Authorization")
|
token := c.Request.Header.Get("Authorization")
|
||||||
|
prefix := strings.HasPrefix(token, "Bearer ")
|
||||||
//// 如果请求头中没有Authorization字段,则尝试从Cookie中获取token
|
if prefix {
|
||||||
//if token == "" {
|
token = strings.TrimPrefix(token, "Bearer ")
|
||||||
// j := NewJWT()
|
}
|
||||||
// token, _ = c.Cookie("x-token")
|
|
||||||
//
|
|
||||||
// // 解析并验证token的有效性
|
|
||||||
// claims, err := j.ParseToken(token)
|
|
||||||
// if err != nil {
|
|
||||||
// // 如果解析失败,记录错误日志并返回当前token
|
|
||||||
// global.Logger.Error("重新写入cookie token失败,未能成功解析token,请检查请求头是否存在x-token且claims是否为规定结构")
|
|
||||||
// return token
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // 如果token有效,则将其重新写入Cookie,并设置过期时间
|
|
||||||
// SetToken(c, token, int((claims.ExpiresAt.Unix()-time.Now().Unix())/60))
|
|
||||||
//}
|
|
||||||
|
|
||||||
// 返回获取到的token
|
// 返回获取到的token
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
@@ -85,9 +46,9 @@ func ClearToken(c *gin.Context) {
|
|||||||
host = c.Request.Host
|
host = c.Request.Host
|
||||||
}
|
}
|
||||||
if net.ParseIP(host) != nil {
|
if net.ParseIP(host) != nil {
|
||||||
c.SetCookie("x-token", "", -1, "/", "", false, false)
|
c.SetCookie("sundynix-token", "", -1, "/", "", false, false)
|
||||||
} else {
|
} else {
|
||||||
c.SetCookie("x-token", "", -1, "/", host, false, false)
|
c.SetCookie("sundynix-token", "", -1, "/", host, false, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
package utils
|
package jwt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"sundynix-go/global"
|
"sundynix-go/global"
|
||||||
"sundynix-go/model/system/request"
|
"sundynix-go/model/system/request"
|
||||||
|
"sundynix-go/utils"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
@@ -31,8 +32,8 @@ func NewJWT() *JWT {
|
|||||||
|
|
||||||
// CreateClaims 创建Claims
|
// CreateClaims 创建Claims
|
||||||
func (j *JWT) CreateClaims(baseClaims request.BaseClaims) request.CustomClaims {
|
func (j *JWT) CreateClaims(baseClaims request.BaseClaims) request.CustomClaims {
|
||||||
bf, _ := ParseDuration(global.Config.JWT.BufferTime)
|
bf, _ := utils.ParseDuration(global.Config.JWT.BufferTime)
|
||||||
ep, _ := ParseDuration(global.Config.JWT.ExpiresTime)
|
ep, _ := utils.ParseDuration(global.Config.JWT.ExpiresTime)
|
||||||
claims := request.CustomClaims{
|
claims := request.CustomClaims{
|
||||||
BaseClaims: baseClaims,
|
BaseClaims: baseClaims,
|
||||||
BufferTime: int64(bf / time.Second), // 缓冲时间1天 缓冲时间内会获得新的token刷新令牌 此时一个用户会存在两个有效令牌 但是前端只留一个 另一个会丢失
|
BufferTime: int64(bf / time.Second), // 缓冲时间1天 缓冲时间内会获得新的token刷新令牌 此时一个用户会存在两个有效令牌 但是前端只留一个 另一个会丢失
|
||||||
Reference in New Issue
Block a user