鉴权实践:Token 解析与路由分组保护
多数业务 API 都要求“先登录、再访问”:客户端带 Token,服务端验签并识别用户。GoFrame 没有强制规定鉴权方案,官方推荐的组合是路由分组 + 中间件:登录接口公开,其余接口挂在受保护分组下,由前置中间件统一完成 Token 校验。Token 签发算法框架不内置,一般用 JWT 类三方库或自实现签名,本章演示自实现的 HMAC Token(教学演示,生产可替换为 JWT)。
基本概念
- 前置中间件在业务 handler 之前执行,决定“放行 / 拒绝”。
r.GetHeader("Authorization")取请求头;r.SetCtxVar("uid", v)把解析结果放进上下文,业务侧用g.RequestFromCtx(ctx).GetCtxVar("uid")读取。- 校验失败的标准做法:返回 401 并
r.Exit()终止后续执行。
登录并签发 Token
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"time"
)
var secret = []byte("please-change-me") // 生产放配置/密钥管理
// SignToken 签发:payload + HMAC-SHA256 签名,演示用,可替换为 JWT
func SignToken(uid int64) string {
payload, _ := json.Marshal(map[string]interface{}{
"uid": uid, "exp": time.Now().Add(2 * time.Hour).Unix(),
})
b64 := base64.RawURLEncoding.EncodeToString(payload)
mac := hmac.New(sha256.New, secret)
mac.Write([]byte(b64))
return b64 + "." + base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
}
中间件解析 Token 并保护分组
package main
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
// Auth 前置中间件:解析 Authorization: Bearer <token>
func Auth(r *ghttp.Request) {
token := r.GetHeader("Authorization")
const prefix = "Bearer "
if len(token) <= len(prefix) || token[:len(prefix)] != prefix {
r.Response.WriteStatus(401, "unauthorized")
r.Exit()
return
}
uid, err := ParseToken(token[len(prefix):]) // 验签并解析,返回 uid
if err != nil {
r.Response.WriteStatus(401, "invalid token")
r.Exit()
return
}
r.SetCtxVar("uid", uid) // 注入上下文,供后续业务使用
r.Middleware.Next()
}
func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.POST("/login", LoginHandler) // 公开:登录接口
})
s.Group("/api", func(group *ghttp.RouterGroup) {
group.Middleware(Auth) // 受保护:/api 下所有接口先过 Auth
group.GET("/profile", ProfileHandler)
})
s.SetPort(8199)
s.Run()
}
业务处理器读取当前用户:
func ProfileHandler(r *ghttp.Request) {
uid := g.RequestFromCtx(r.Context()).GetCtxVar("uid")
r.Response.WriteJson(g.Map{"uid": uid.Val()})
}
规范路由下的鉴权
规范路由(group.Bind)同样把鉴权中间件挂到分组即可;校验失败的返回建议对齐统一响应格式(如 code != 0 的 JSON,而非裸 401 文本),可参考“统一响应与错误码”章节。框架也内置 Session 能力(r.Session),简单后台系统可直接用 Session 方案,官方文档均有对应章节。
注意点
- Token 过期与“注销即失效”是两件事:无状态 Token 注销即时性差,必要时引入 Redis 黑名单。
- 鉴权放在 CORS/日志等通用中间件之后、业务 handler 之前;401 是“未认证”,403 是“无权限”,要区分清楚。
小结
鉴权骨架 = 登录发 Token + group.Middleware(Auth) 保护分组 + SetCtxVar 传递身份。无论换 JWT 库还是自研签名,中间件边界与 401 处理都是同一套,掌握后可平滑替换实现。