你的项目中使用到了gtoken,说一下你是怎么使用它来完成登录鉴权这个功能的
后台的登录鉴权完全依托 gtoken 中间件。服务启动时先调用 StartBackendGToken()
生成 *gtoken.GfToken
实例,并将其 Middleware(ctx, backendGroup)
挂到 /backend
路由组;这里配置了 LoginPath
为 /login
、LogoutPath
为 /user/logout
,以及三大回调:loginFunc
、loginAfterFunc
、authAfterFunc
。当客户端 POST /backend/login
时,gtoken 将请求交给 loginFunc
,函数内部读取 name/password
,到 admin_info
表验证加盐 MD5 密码,成功后返回 userKey
(如 Admin:123
)和整条 adminInfo
数据;gtoken 立即基于 userKey
生成随机 Token,将 Token→userKey、userKey→userData 写入缓存,然后执行 loginAfterFunc
。在 loginAfterFunc
里我先解析 userKey
得到管理员 ID,再查 role_permission_info
与 permission_info
表拼出完整权限列表,最终组装 backend.LoginRes{Token, ExpireIn, IsAdmin, RoleIds, Permissions}
,通过 response.JsonExit(r, 0, "", data)
写回前端。后续任何 /backend
请求都会带 Authorization: Bearer <token>
头部,先经过 gtoken 中间件校验 Token;校验通过后触发 authAfterFunc
,该回调把 adminId/adminName/isAdmin/roleIds
等字段写入 ctx
(r.SetCtxVar
),业务控制器即可直接从上下文获取当前管理员信息而无需再查库。调用 /backend/user/logout
时,gtoken 删除缓存中的 Token 记录,令其即时失效。整个实现只需维护三段回调代码,其余 Token 生成、存储、验证、过期刷新等细节全部由 gtoken 完成。
那你再讲一下前台用户登录的实现吧
前台这块登录我还是用 gtoken,但跟后台完全隔离。我在启动时又实例化了一套 GfToken
,挂在 /frontend
路由组里,配置的 LoginPath
还是 /login
,于是完整的登录地址变成 /frontend/login
。当用户在 App 或 H5 端提交账号密码,gtoken 会把请求转给我写的 loginFuncFrontend
:我先查 user_info
表,看用户名是否存在,再用同样的盐 + 双 MD5 算法校验密码,校验通过后返回一串 userKey
(前缀我定义成 User:
,例如 User:456
)以及整条 userInfo
数据。gtoken 收到这两个值会立即生成随机 Token,把 Token→userKey、userKey→userInfo 两级映射写进缓存,然后调用 loginAfterFuncFrontend
。在这个回调里我做两件事:第一,把 userKey
截出纯用户 ID,再查一次数据库拿用户的头像、签名、状态;第二,把 Token、过期时间和这些信息封装进前端专用的 LoginRes
结构体,用 response.JsonExit
直接写回客户端。客户端拿到 Token 之后,后面的收藏、购物车、下单等接口都会在请求头里带 Authorization: Bearer <token>
。这些接口由于被套在 frontendToken.Middleware
下面,进来先走 gtoken 的 Token 校验,校验成功后会触发 authAfterFuncFrontend
,我在这里把用户的 ID、昵称、头像、状态等塞进 context
,这样业务层用 g.RequestFromCtx(ctx).GetCtxVar()
就能直接拿到当前登录人的信息,整个流程就闭环了。若用户点击退出 /frontend/user/logout
,gtoken会把缓存里的 Token 删掉,旧 Token 马上失效,实现了完整的前台登录、鉴权和注销。
下一个问题:为什么没有选择jwt,gtoken相比于jwt有什么优势吗
在这个项目里我最后没有用纯粹的 jwt 中间件,主要是因为 gtoken 已经把“登录校验、Token 生成、刷新、缓存、多端踢号、鉴权回调”这些常用功能全部打包好了,落地成本更低:我只需要实现三个回调函数就能覆盖登录到鉴权的全链路,而如果用 jwt,我还得自己写登录接口、生成签名、管理黑名单或 Redis 列表、处理多端互踢、写中间件去解析和续期,工作量会翻倍;再者,gtoken 的两级缓存(Token→userKey,userKey→userData)天然支持一次性失效某个用户全部 Token,这在后台管理系统里“强制下线”很常见,而 jwt 想实现同样能力必须额外存黑名单或者改 token 字段;同时,gtoken 内置了 MultiLogin、LogoutPath、AuthExcludePaths 这些开箱即用的选项,让我可以在配置层面就决定“允许不允许多端登录”或“哪些路由跳过鉴权”,不用自己写逻辑判断,所以综合易用性、功能完备度和二次开发量,gtoken 对这个电商后台来说比裸 jwt 更合适。
什么是gtoken的两级缓存
在 gtoken 里,登录状态是靠“两级缓存”来保存的:第一层用 Token 去映射 userKey,第二层再用 userKey 去映射完整的用户数据。也就是说,缓存里同时存在两条记录——token:<随机串>
存的只是 User:456
这种 userKey,而 user:<User:456>
才存序列化后的 userInfo。这样设计的好处是显而易见的:当拿到请求里的 Token 时,先查第一层就能迅速确定“这是谁”;如果想把某个用户的所有 Token 一次性失效,只要删掉第二层那条 userKey 记录,关联的所有 Token 都找不到主人自然就失效了;反过来,如果只是刷新有效期或者单点踢下线,只操作第一层也不会触碰用户资料。相比只存一张 “Token→用户信息” 的表,这种分层让多端登录管理、单端踢号和数据续期都简单得多,同时也减少了并发场景下的缓存膨胀问题。
想象你在电影院存包。
• 每位观众都拿到一张 寄存小票(Token)。
小票正面只写一个“柜箱编号”(userKey),比如「柜箱 A17」,方便你之后凭票取包。
• 而真正的 包裹(userData)是放在编号 A17 的储物柜里,里面才有你的包、衣服、手机等全部物品。
两级对应关系:
小票 → 柜箱编号 (Token → userKey)
只要看到小票,就能快速知道去哪个柜子。柜箱编号 → 包裹内容 (userKey → userData)
打开对应柜子,才能拿到你的全部物品。
这样做带来的好处:
• 如果工作人员要“作废一张小票”,只需收回那张票(删掉 Token),柜里的包不受影响。
• 如果某位观众忘带身份证被要求全部物品重新安检,工作人员直接清空柜子 A17(删除 userKey 对应数据),那位观众手里所有小票都失效,因为他们再也对应不到包裹。
• 想延长寄存时间,只改小票上的有效期就行,不用动柜里的包。
gtoken 的两级缓存就是这套寄存逻辑在代码里的翻版:
第一张“小票”快定位用户,第二层“柜子”保存完整用户信息,既能单独作废票,也能一键清空整柜。