Golang Gin框架JWT认证跨域预检401错误求助
解决Gin + JWT 跨域预请求401问题
嘿,这个问题我太熟了!咱们先来搞清楚为啥Postman正常但Chrome报错:Postman不会发送预请求(OPTIONS请求),但浏览器在发送跨域的POST/GET等请求前,会先自动发一个OPTIONS请求去确认服务器是否允许跨域。你的JWT认证中间件拦截了这个OPTIONS请求——毕竟OPTIONS里不会带Authorization token嘛,所以直接返回了401,导致预请求失败,后续的真实请求根本发不出去。
下面给你一套完整的解决方案,分后端配置和前端调整两步来:
一、后端Gin配置正确的CORS中间件
你需要确保Gin能正确处理OPTIONS请求,并且允许前端的域名、必要的请求头和方法。这里有两种方式:
方式1:使用官方推荐的gin-contrib/cors包
这是最省心的方式,先安装包:
go get github.com/gin-contrib/cors
然后在你的Gin初始化代码里配置:
package main import ( "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() // 配置CORS规则 corsConfig := cors.DefaultConfig() // 替换成你的前端实际域名,比如http://localhost:3000 corsConfig.AllowOrigins = []string{"http://localhost:你的前端端口"} // 允许所有常用的请求方法 corsConfig.AllowMethods = []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"} // 允许必要的请求头,尤其是Authorization(JWT token用)和Content-Type corsConfig.AllowHeaders = []string{"Origin", "Content-Type", "Authorization"} // 允许携带凭证(比如cookie或token) corsConfig.AllowCredentials = true // 设置预请求缓存时间,减少OPTIONS请求的次数 corsConfig.MaxAge = 12 * 3600 // 注册CORS中间件,要放在所有路由和认证中间件之前! r.Use(cors.New(corsConfig)) // 接下来注册你的路由 r.POST("/login", loginHandler) // 认证中间件放在私有路由前面 r.GET("/v1/hello", authMiddleware, helloHandler) r.Run(":8080") }
方式2:自己手写CORS中间件
如果不想用第三方包,也可以自己写一个:
func corsMiddleware() gin.HandlerFunc { return func(c *gin.Context) { // 获取请求的Origin头,允许跨域 origin := c.Request.Header.Get("Origin") c.Writer.Header().Set("Access-Control-Allow-Origin", origin) // 允许的请求方法 c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") // 允许的请求头 c.Writer.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Authorization") // 允许携带凭证 c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") // 预请求缓存12小时 c.Writer.Header().Set("Access-Control-Max-Age", "43200") // 如果是OPTIONS预请求,直接返回200,不进入后续中间件 if c.Request.Method == "OPTIONS" { c.AbortWithStatus(200) return } // 非OPTIONS请求,继续执行后续逻辑 c.Next() } } // 然后在main里注册这个中间件(同样要放在最前面) func main() { r := gin.Default() r.Use(corsMiddleware()) // ... 注册路由和认证中间件 }
二、调整JWT认证中间件,放过OPTIONS请求
不管你用哪种CORS配置,都要确保你的JWT认证中间件不会拦截OPTIONS请求。修改你的authMiddleware:
func authMiddleware() gin.HandlerFunc { return func(c *gin.Context) { // 先判断是不是OPTIONS请求,是的话直接放行 if c.Request.Method == "OPTIONS" { c.Next() return } // 下面是你的JWT认证逻辑 tokenStr := c.GetHeader("Authorization") // 比如去掉Bearer前缀,验证token... // 验证失败返回401,验证通过继续执行 c.Next() } }
三、前端Ajax请求调整
确保你的jQuery/Ajax请求设置了withCredentials: true,这样才能携带Authorization头和凭证:
// 登录请求示例 $.ajax({ url: "http://localhost:8080/login", type: "POST", data: { username: "your-username", password: "your-password" }, xhrFields: { withCredentials: true }, success: function(res) { // 保存token const token = res.token; // 调用私有接口 callPrivateApi(token); } }); // 私有接口调用示例 function callPrivateApi(token) { $.ajax({ url: "http://localhost:8080/v1/hello", type: "GET", headers: { "Authorization": "Bearer " + token }, xhrFields: { withCredentials: true }, success: function(data) { console.log("私有接口返回:", data); }, error: function(xhr) { console.error("请求失败:", xhr.responseText); } }); }
为啥浏览器扩展没用?
很多CORS扩展只是简单添加Access-Control-Allow-Origin: *,但没有正确处理OPTIONS请求的响应状态,或者你的后端认证中间件还是会拦截OPTIONS请求,所以根本问题还是要从后端配置入手,而不是依赖浏览器扩展。
内容的提问来源于stack exchange,提问作者Medo




