登录路由必须用POST方法注册,因GET会明文暴露密码于URL、日志和代理中,PUT/DELETE违背REST语义且前端支持差;正确做法是r.POST("/login", handler),配合ShouldBind解析结构体并校验密码,区分API与页面渲染响应方式,避开中间件误拦截。

登录路由必须用 POST 方法注册
Gin 的 POST 路由是处理登录请求的唯一合理选择。用 GET 传密码会明文暴露在 URL、日志和代理中,PUT 或 DELETE 则违背 REST 语义且多数前端库默认不支持携带 body。实际开发中常见错误是写成 r.GET("/login", loginHandler),结果密码被浏览器地址栏记住、CDN 缓存甚至出现在 Nginx access log 里。
正确做法是:
- 始终用
r.POST("/login", loginHandler) - 前端用
fetch或axios发送 JSON 或表单数据(Content-Type: application/x-www-form-urlencoded) - 服务端用
c.ShouldBind()解析结构体,别手动调c.PostForm()拼接——容易漏校验、难复用
登录 handler 必须校验密码但不能明文比对
直接用 == 比对明文密码是严重安全漏洞。Gin 本身不处理密码逻辑,但你得在 handler 里集成可靠方案。常见错误包括:从数据库查出用户后用 user.Password == input.Password,或用 bcrypt.CompareHashAndPassword 时没检查返回的 error 值(比如哈希格式错误时会 panic)。
推荐结构:
- 先用
c.ShouldBind(&loginReq)解析请求,失败立即c.AbortWithStatusJSON(400, ...) - 查用户(注意防 SQL 注入,用 GORM 的
Where("username = ?", loginReq.Username)) - 查到后调
bcrypt.CompareHashAndPassword(user.HashedPassword, []byte(loginReq.Password)),必须判断 error 是否为bcrypt.ErrMismatchedHashAndPassword - 成功后生成 token(如 JWT),**不要把密码、盐、原始哈希存进 session 或 cookie**
登录成功后跳转或返回 token 要区分场景
API 服务和传统网页应用的响应方式完全不同,混用会导致前端反复 401 或跳转失效。典型错误是给 SPA 返回 c.Redirect(302, "/dashboard"),但前端是 axios 请求,跳转完全不生效;或者给服务端渲染页面返回 JSON,导致浏览器弹出下载框。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
判断依据很简单:
- 看请求头:
c.GetHeader("X-Requested-With") == "XMLHttpRequest"或检查Accept是否含application/json - 纯 API 场景:用
c.JSON(200, map[string]interface{}{"token": jwtStr}) - 服务端渲染(如 Gin + HTML 模板):用
c.SetCookie()写 session ID,再c.Redirect(302, "/home") - 千万别在同一个 handler 里既写 cookie 又返回 JSON —— 前端无法同时处理
登录路由要避开中间件拦截陷阱
很多项目在全局加了 authMiddleware,结果把 /login 也拦住了,导致永远 401。这不是 Gin 的问题,是你注册顺序或路径匹配逻辑错了。另一个坑是用了 r.Use() 全局挂中间件,却忘了 /login 需要单独用 r.POST("/login", loginHandler) 而不是塞进分组里。
安全又清晰的做法:
- 用
authorized := r.Group("/api")分组受保护路由,authorized.Use(authMiddleware) - 登录路由单独注册在根组:
r.POST("/login", loginHandler) - 如果必须分组,用
unauthorized := r.Group("")显式声明免认证路由组 - 检查中间件里是否误判了
c.Request.URL.Path—— 比如写了strings.HasPrefix(c.Request.URL.Path, "/api")却没排除/api/login
最常被忽略的是:JWT 过期时间写死在 handler 里,而不是配置项;还有忘记在登录成功后清除旧 session,导致一个账号多端登录时互相踢下线不可控。


















