Golang MySQL服务重定向时出现CORS跨域请求阻塞错误排查
解决跨域登录重定向的CORS错误及Go服务优化
核心问题分析
- 跨域AJAX请求中,后端返回的重定向(3xx状态码)不会被浏览器自动执行,浏览器会拦截响应并触发CORS错误——重定向后的资源依然跨域,且当前代码的CORS头设置未覆盖重定向场景,导致preflight检查失败。
- 你当前的逻辑是后端直接重定向,但跨域场景下这种方式不适用,浏览器不会跟随重定向跳转,反而会因为响应头不符合CORS规则报错。
解决方案:前端主导跳转(推荐)
跨域场景下,后端应返回登录结果的JSON数据,由前端页面根据结果自行处理跳转。修改loginHandler代码如下:
func loginHandler(w http.ResponseWriter, r *http.Request) { log.Println("Received request to loginHandler") // 确保所有响应都携带CORS头 w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Headers", "Content-Type") w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS") if r.Method == http.MethodOptions { w.WriteHeader(http.StatusOK) return } if r.Method != http.MethodPost { http.Error(w, "Invalid request method", http.StatusMethodNotAllowed) return } body, err := ioutil.ReadAll(r.Body) if err != nil { log.Println("Error reading request body:", err) http.Error(w, "Failed to read request body", http.StatusInternalServerError) return } if len(body) == 0 { http.Error(w, "Empty request body", http.StatusBadRequest) return } var creds Credentials err = json.Unmarshal(body, &creds) if err != nil { log.Println("Error decoding request body:", err) http.Error(w, "Invalid request body", http.StatusBadRequest) return } log.Printf("Received credentials: %+v", creds) success, err := CheckCredentials(creds) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // 返回JSON结果,交由前端处理跳转 w.Header().Set("Content-Type", "application/json") if success { json.NewEncoder(w).Encode(map[string]interface{}{ "success": true, "redirectUrl": "/gui/dashboard.html", }) } else { json.NewEncoder(w).Encode(map[string]interface{}{ "success": false, "redirectUrl": "/gui/index.html", }) } }
前端配套处理示例(JS):
fetch('http://localhost:8080/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({username: '你的用户名', password: '你的密码'}) }) .then(res => res.json()) .then(data => { window.location.href = data.redirectUrl; }) .catch(err => console.error(err));
额外优化建议
- 数据库连接池优化
当前CheckCredentials每次请求都打开/关闭数据库连接,性能极低。建议初始化全局连接池复用连接:
// 全局连接池,程序启动时初始化 var db *sql.DB func init() { var err error db, err = sql.Open("mysql", "username:password@tcp(127.0.0.1:3306)/dbname") if err != nil { log.Fatal("Failed to init database:", err) } // 设置连接池参数 db.SetMaxOpenConns(20) db.SetMaxIdleConns(10) } func CheckCredentials(creds Credentials) (bool, error) { log.Println("Checking if the username and password are valid and not expired") var count int // 使用全局连接池查询 err := db.QueryRow("SELECT COUNT(*) FROM users WHERE username = ? AND password = ? AND expires_at > NOW()", creds.Username, creds.Password).Scan(&count) if err != nil { log.Println("Failed to query the database:", err) return false, fmt.Errorf("failed to query the database: %w", err) } return count > 0, nil }
- 密码安全优化
代码中直接存储和传输明文密码,存在严重安全隐患。应使用bcrypt哈希算法存储密码,验证时比对哈希值:
// 存储用户时生成密码哈希 hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(rawPassword), bcrypt.DefaultCost) // 登录验证时比对哈希 err := bcrypt.CompareHashAndPassword([]byte(storedHash), []byte(creds.Password)) if err == nil { // 密码匹配成功 }
- CORS头安全性细化
不要使用*作为Access-Control-Allow-Origin,应指定具体的前端域名,避免任意跨域请求:
w.Header().Set("Access-Control-Allow-Origin", "http://你的前端域名:端口")
内容的提问来源于stack exchange,提问作者jimmy white
相关产品推荐
相关产品推荐

