cors中间件必须置于jwt中间件之前,因预检options请求不带authorization头,若jwt中间件前置会直接返回401或panic,导致预检失败、后续请求被浏览器拦截。

Go 语言里做跨域 + JWT 鉴权,不是“配个中间件就完事”,关键在 gin(或其他框架)中对 Authorization 头的提取时机、签名密钥的生命周期管理、以及 CORS 预检请求与 JWT 解析的执行顺序——这三处出错,90% 的“跨域登录失败”“token 总是 invalid”问题就来了。
为什么 Gin 的 CORS 中间件必须放在 JWT 中间件之前?
CORS 预检请求(OPTIONS)不带 Authorization 头,但 JWT 中间件若提前执行,会直接返回 401 或 panic,导致预检失败,浏览器后续的 POST/GET 请求根本发不出去。
-
gin.Default()自带的gin.Recovery()和gin.Logger()可以保留,但自定义中间件顺序必须是:cors.New(...)→jwtAuthMiddleware→ 路由注册 - 用
github.com/rs/cors更稳妥:它默认跳过OPTIONS请求的后续中间件链,而gin-contrib/cors若配置不当(比如没设AllowAllOrigins: true或漏了AllowHeaders),仍可能拦截 - 务必在
AllowHeaders中显式包含"Authorization"和"Content-Type",否则 Chrome/Firefox 会拒绝携带 token 的请求
jwt.Parse 报 signature is invalid 的真实原因
这不是密钥写错了那么简单。常见真因是:前端传来的 token 带了 "Bearer " 前缀,而你的解析代码直接拿整个字符串去验签;或者密钥类型不匹配([]byte vs string),或用了 v4 库却按 v5 文档写法调用。
- 提取 token 时必须切掉前缀:
authHeader := c.GetHeader("Authorization"); tokenString := strings.TrimPrefix(authHeader, "Bearer ") - 密钥必须统一为
[]byte类型,哪怕你存的是字符串常量,也要写成[]byte("your-secret-key"),v5 版本的KeyFunc返回值类型是any,但实际只认[]byte或*rsa.PrivateKey等具体类型 - 别混用 v4 和 v5:v5 的
jwt.MapClaims是类型别名,但jwt.ParseWithClaims的第三个参数签名已变,v4 用func(*jwt.Token) (interface{}, error),v5 改为jwt.Keyfunc,即func(token *jwt.Token) (any, error)
JWT payload 里放什么字段才真正安全又实用?
别把敏感字段(如密码哈希、手机号)塞进 payload——它只是 base64url 编码,不是加密,任何人截获 token 都能 decode 看见内容。重点是用好标准声明(registered claims)和签名防篡改。
- 必设
"exp"(过期时间戳),且服务端解析时要主动检查:claims.VerifyExpiresAt(time.Now().Add(5*time.Second), true)—— 这个true表示允许 5 秒斜率容错,避免客户端和服务端时间不同步导致误判 - 推荐加
"jti"(JWT ID)字段,配合 Redis 黑名单实现单次 token 吊销;单纯依赖"exp"无法应对 token 泄露后的即时失效 - 业务字段如
"user_id"、"role"可放,但别放可被用户伪造的权限标识(如"is_admin": true),应从数据库查角色再比对
最易被忽略的点:生产环境绝不能硬编码密钥,os.Getenv("JWT_SECRET_KEY") 要配合非空校验;另外,SigningMethodHS256 要求密钥长度 ≥32 字节,短密钥会导致签名被暴力破解——这点连很多 demo 都没提。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











