Files
smartmate/backend/middleware/token_handler.go
Losita e6941f98f2 Version: 0.7.4.dev.260323
 feat(schedulerefine): 新增 refine 子路由,优先执行复合操作,失败后降级至禁复合 ReAct 兜底

ReAct 升级
- ♻️ 将原有链路升级为真正的 ReAct 执行模式,进一步增强整体调度过程的可靠性

Refine 子路由
- 🧭 在 refine 主链路中新增 `route` 节点,整体流程调整为 `contract -> plan -> slice -> route -> react -> hard_check -> summary`
-  当 `route` 命中全局复合目标时,优先尝试一次调用 `SpreadEven` / `MinContextSwitch`,失败后最多重试 2 次
- 🔀 `route` 成功后直接跳过 `ReAct`;若执行失败,则自动切换至 `fallback` 模式
- 🛡️ 在 `fallback` 模式下增加后端硬约束:禁用 `SpreadEven` / `MinContextSwitch` / `BatchMove`,仅允许使用 `Move` / `Swap` 逐任务处理
- 🧠 在 `ReAct` 的 prompt 与上下文中新增 `COMPOSITE_TOOLS_ALLOWED`,显式告知当前是否允许使用复合工具
- 🧩 扩展状态字段以承载路由与降级状态:`CompositeRetryMax` / `DisableCompositeTools` / `CompositeRouteTried` / `CompositeRouteSucceeded`
- 👀 增加 `route` 相关阶段日志,便于排查命中、重试、收口与降级原因

修复
- 🐛 修复 JWT Token 过期时间未按 `config.yaml` 配置生效的问题

备注
- 🚧 当前 ReAct 逐步微排链路已趋于稳定,但两个复合操作函数仍未恢复可用,后续将继续排查
2026-03-23 23:14:19 +08:00

104 lines
2.9 KiB
Go
Raw 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 middleware
import (
"errors"
"net/http"
"strings"
"github.com/LoveLosita/smartflow/backend/auth"
"github.com/LoveLosita/smartflow/backend/dao"
"github.com/LoveLosita/smartflow/backend/model"
"github.com/LoveLosita/smartflow/backend/respond"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v4"
)
// extractTokenFromAuthorization 负责解析 Authorization 头中的 token。
// 职责边界:
// 1. 兼容“裸 token”和“Bearer <token>”两种传参方式。
// 2. 不负责 token 合法性校验,只做字符串提取。
// 3. 输入输出语义header 为空或格式非法时返回空字符串。
func extractTokenFromAuthorization(header string) string {
trimmed := strings.TrimSpace(header)
if trimmed == "" {
return ""
}
parts := strings.Fields(trimmed)
if len(parts) == 2 && strings.EqualFold(parts[0], "Bearer") {
return strings.TrimSpace(parts[1])
}
if len(parts) == 1 {
return parts[0]
}
return ""
}
// JWTTokenAuth 负责 access token 的鉴权拦截。
// 职责边界:
// 1. 负责解析 token、验签、校验 token_type 与黑名单状态。
// 2. 不负责签发 token也不负责用户登录逻辑。
// 3. 输出语义:校验通过时写入 user_id/claims 到上下文并放行;失败则中断请求。
func JWTTokenAuth(cache *dao.CacheDAO) gin.HandlerFunc {
return func(c *gin.Context) {
tokenString := extractTokenFromAuthorization(c.GetHeader("Authorization"))
if tokenString == "" {
c.JSON(http.StatusUnauthorized, respond.MissingToken)
c.Abort()
return
}
accessKey, err := auth.AccessSigningKey()
if err != nil {
c.JSON(http.StatusInternalServerError, respond.InternalError(err))
c.Abort()
return
}
// 1. 先验签并由 jwt 库统一校验 exp 等标准声明。
token, err := jwt.ParseWithClaims(tokenString, &model.MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, respond.InvalidTokenSingingMethod
}
return accessKey, nil
})
if err != nil || !token.Valid {
c.JSON(http.StatusUnauthorized, respond.InvalidToken)
c.Abort()
return
}
// 2. 再做业务声明校验,防止 refresh token 越权访问业务接口。
claims, ok := token.Claims.(*model.MyCustomClaims)
if !ok {
c.JSON(http.StatusUnauthorized, respond.InvalidClaims)
c.Abort()
return
}
if claims.TokenType != "access_token" {
c.JSON(http.StatusUnauthorized, respond.WrongTokenType)
c.Abort()
return
}
// 3. 最后查黑名单,兜住“用户已登出但 token 仍未到期”的场景。
isBlack, err := cache.IsBlacklisted(claims.Jti)
if err != nil {
c.JSON(http.StatusInternalServerError, respond.InternalError(errors.New("无法验证令牌状态")))
c.Abort()
return
}
if isBlack {
c.JSON(http.StatusUnauthorized, respond.UserLoggedOut)
c.Abort()
return
}
c.Set("user_id", claims.UserID)
c.Set("claims", claims)
c.Next()
}
}