★ 鉴权实践:中间件+Token

面试问法:GoFrame 有内置鉴权方案吗?登录态与接口保护一般怎么设计? 一句话结论:框架不内置账号体系与 JWT,但提供了完整的中间件与上下文机制:登录接口签发 Token,受保护分组挂一个前置鉴权中间件,校验通过后 r.SetCtxVar 把身份放进请求上下文,业务侧从 ctx 取;要“注销即时失效”再引入 Redis 黑名单或会话版本号。

关键 API

能力用法
取请求头r.GetHeader("Authorization")
终止请求r.Response.WriteStatus(401, msg) + r.Exit()
注入身份r.SetCtxVar("uid", uid)
业务侧读取g.RequestFromCtx(ctx).GetCtxVar("uid")
分组保护group.Middleware(Auth),规范路由同样适用

最短示例

// 登录成功后签发 Token:可自研签名或引入 JWT 类库,框架不内置
token := SignToken(user.Id)

// 鉴权中间件:解析 "Bearer <token>" 得到 uid
func Auth(r *ghttp.Request) {
    raw := r.GetHeader("Authorization")
    token := strings.TrimPrefix(raw, "Bearer ")
    if token == raw { // 无 Bearer 前缀
        r.Response.WriteStatus(401, "unauthorized")
        r.Exit()
        return
    }
    uid, err := ParseToken(token) // 验签 + 解析
    if err != nil {
        r.Response.WriteStatus(401, "invalid token")
        r.Exit()
        return
    }
    r.SetCtxVar("uid", uid)
    r.Middleware.Next()
}

s.Group("/", func(g *ghttp.RouterGroup) { g.POST("/login", Login) }) // 公开
s.Group("/api", func(g *ghttp.RouterGroup) {                          // 受保护
    g.Middleware(Auth)
    g.Bind(new(v1.UserController))
})

业务侧取当前用户:uid := g.RequestFromCtx(ctx).GetCtxVar("uid").Int();controller/service/dao 一路传 ctx,身份随处可得。

设计注意

  • 401(未认证)与 403(无权限)要分开;鉴权失败返回建议对齐统一响应格式(code != 0 的 JSON,而非裸文本)。
  • 白名单路由(/login、/swagger、健康检查)放公开分组,或在中间件内按路径放行。
  • 无状态 Token“注销不即时”:需要时用 Redis 黑名单/会话版本号;纯内部后台可退而用框架内置 Session(r.Session)。
  • 中间件顺序:CORS 之后、业务 handler 之前;过期时间由签发端控制(如 2h)。

常见追问

  • 追问:Token 放 header 还是 cookie?——header 适合前后端分离与多端;cookie + HttpOnly 能防 XSS 窃取但要防 CSRF,按威胁模型选择。
  • 追问:怎么支持“踢人下线 / 单点登录”?——Token 绑定会话版本号或维护 Redis 在线表,每次校验比对,即可即时失效。

记忆点

  • 公式:登录发 Token + group.Middleware(Auth) + SetCtxVar 传递身份。
  • 框架给机制不给方案:签名算法、黑名单、RBAC 权限模型都要业务自己组装。
笔记加载中…