init
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/goravel/framework/contracts/http"
|
||||
"github.com/goravel/framework/facades"
|
||||
"github.com/goravel/framework/support/str"
|
||||
|
||||
"goravel/app/http/trans"
|
||||
"goravel/app/models"
|
||||
"goravel/app/services"
|
||||
"goravel/app/utils/logger"
|
||||
)
|
||||
|
||||
// min 返回两个整数中的较小值
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func Jwt() http.Middleware {
|
||||
return func(ctx http.Context) {
|
||||
// 如果路径是api/admin前缀,使用admin guard
|
||||
path := ctx.Request().Path()
|
||||
pathStr := str.Of(path)
|
||||
if pathStr.IsEmpty() || (!pathStr.StartsWith("/api/admin") && !pathStr.StartsWith("/admin")) {
|
||||
ctx.Request().Next()
|
||||
return
|
||||
}
|
||||
|
||||
token := ctx.Request().Header("Authorization", "")
|
||||
|
||||
// 如果 Header 中没有 token,尝试从 URL 参数中获取(用于 SSE 等不支持自定义 headers 的场景)
|
||||
if str.Of(token).IsEmpty() {
|
||||
token = ctx.Request().Query("_token", "")
|
||||
}
|
||||
|
||||
if str.Of(token).IsEmpty() {
|
||||
_ = ctx.Response().Json(http.StatusUnauthorized, http.Json{
|
||||
"code": http.StatusUnauthorized,
|
||||
"message": trans.Get(ctx, "not_logged_in"),
|
||||
}).Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 移除Bearer前缀(如果有)
|
||||
token = str.Of(token).ChopStart("Bearer ").Trim().String()
|
||||
|
||||
if token == "" {
|
||||
_ = ctx.Response().Json(http.StatusUnauthorized, http.Json{
|
||||
"code": http.StatusUnauthorized,
|
||||
"message": trans.Get(ctx, "not_logged_in"),
|
||||
}).Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 从数据库查找token
|
||||
tokenService := services.NewTokenServiceImpl()
|
||||
accessToken, err := tokenService.FindToken(token)
|
||||
if err != nil {
|
||||
// token查找失败或已过期
|
||||
logger.ErrorfHTTP(ctx, "JWT middleware: FindToken error: %v, token prefix: %s", err, token[:min(20, len(token))])
|
||||
_ = ctx.Response().Json(http.StatusUnauthorized, http.Json{
|
||||
"code": http.StatusUnauthorized,
|
||||
"message": trans.Get(ctx, "invalid_token"),
|
||||
}).Abort()
|
||||
return
|
||||
}
|
||||
if accessToken == nil {
|
||||
logger.ErrorfHTTP(ctx, "JWT middleware: accessToken is nil, token prefix: %s", token[:min(20, len(token))])
|
||||
_ = ctx.Response().Json(http.StatusUnauthorized, http.Json{
|
||||
"code": http.StatusUnauthorized,
|
||||
"message": trans.Get(ctx, "invalid_token"),
|
||||
}).Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 检查token类型
|
||||
if accessToken.TokenableType != "admin" {
|
||||
_ = ctx.Response().Json(http.StatusUnauthorized, http.Json{
|
||||
"code": http.StatusUnauthorized,
|
||||
"message": trans.Get(ctx, "invalid_token"),
|
||||
}).Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 查询用户信息
|
||||
var admin models.Admin
|
||||
if err := facades.Orm().Query().Where("id", accessToken.TokenableID).First(&admin); err != nil {
|
||||
_ = ctx.Response().Json(http.StatusUnauthorized, http.Json{
|
||||
"code": http.StatusUnauthorized,
|
||||
"message": trans.Get(ctx, "user_not_found"),
|
||||
}).Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 更新最后使用时间
|
||||
_ = tokenService.UpdateLastUsedAt(token)
|
||||
|
||||
// 滑动过期:如果token有过期时间,每次请求时自动延长过期时间
|
||||
if accessToken.ExpiresAt != nil {
|
||||
ttl := facades.Config().GetInt("jwt.ttl", 60) // 默认60分钟
|
||||
if ttl > 0 {
|
||||
newExpiresAt := time.Now().Add(time.Duration(ttl) * time.Minute)
|
||||
// 更新token的过期时间
|
||||
_, _ = facades.Orm().Query().
|
||||
Model(&models.PersonalAccessToken{}).
|
||||
Where("id", accessToken.ID).
|
||||
Update("expires_at", newExpiresAt)
|
||||
}
|
||||
}
|
||||
|
||||
// 将用户信息存储到context中,供后续中间件使用
|
||||
ctx.WithValue("admin", admin)
|
||||
ctx.WithValue("token", accessToken)
|
||||
|
||||
// facades.Log().Debugf("JWT middleware: admin set in context, ID: %d, Username: %s", admin.ID, admin.Username)
|
||||
|
||||
ctx.Request().Next()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user