GoFrame电商项目面试

发布于:2025-07-08 ⋅ 阅读:(14) ⋅ 点赞:(0)

你的项目中使用到了gtoken,说一下你是怎么使用它来完成登录鉴权这个功能的

后台的登录鉴权完全依托 gtoken 中间件。服务启动时先调用 StartBackendGToken() 生成 *gtoken.GfToken 实例,并将其 Middleware(ctx, backendGroup) 挂到 /backend 路由组;这里配置了 LoginPath/loginLogoutPath/user/logout,以及三大回调:loginFuncloginAfterFuncauthAfterFunc。当客户端 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_infopermission_info 表拼出完整权限列表,最终组装 backend.LoginRes{Token, ExpireIn, IsAdmin, RoleIds, Permissions},通过 response.JsonExit(r, 0, "", data) 写回前端。后续任何 /backend 请求都会带 Authorization: Bearer <token> 头部,先经过 gtoken 中间件校验 Token;校验通过后触发 authAfterFunc,该回调把 adminId/adminName/isAdmin/roleIds 等字段写入 ctxr.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 的储物柜里,里面才有你的包、衣服、手机等全部物品。

两级对应关系:

  1. 小票 → 柜箱编号 (Token → userKey)
    只要看到小票,就能快速知道去哪个柜子。

  2. 柜箱编号 → 包裹内容 (userKey → userData)
    打开对应柜子,才能拿到你的全部物品。

这样做带来的好处:

• 如果工作人员要“作废一张小票”,只需收回那张票(删掉 Token),柜里的包不受影响。
• 如果某位观众忘带身份证被要求全部物品重新安检,工作人员直接清空柜子 A17(删除 userKey 对应数据),那位观众手里所有小票都失效,因为他们再也对应不到包裹。
• 想延长寄存时间,只改小票上的有效期就行,不用动柜里的包。

gtoken 的两级缓存就是这套寄存逻辑在代码里的翻版:
第一张“小票”快定位用户,第二层“柜子”保存完整用户信息,既能单独作废票,也能一键清空整柜。


网站公告

今日签到

点亮在社区的每一天
去签到