主要原因
CORS 策略限制:默认情况下,跨域请求不会发送凭证(如 Cookie)
Cookie 设置问题:服务器端 Cookie 设置不当
前端配置缺失:XMLHttpRequest 或 Fetch API 未正确配置
完整解决方案
1. 前端配置
XMLHttpRequest (传统 AJAX)
const xhr = new XMLHttpRequest();
xhr.withCredentials = true; // 关键设置
xhr.open('POST', 'https://api.example.com/login', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({username: 'user', password: 'pass'}));
Fetch API
fetch('https://api.example.com/login', {
method: 'POST',
credentials: 'include', // 关键设置
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({username: 'user', password: 'pass'})
});
jQuery AJAX
$.ajax({
url: 'https://api.example.com/login',
type: 'POST',
xhrFields: {
withCredentials: true // 关键设置
},
crossDomain: true,
data: JSON.stringify({username: 'user', password: 'pass'}),
contentType: 'application/json',
success: function(response) {
console.log('登录成功');
}
});
Axios
axios.post('https://api.example.com/login',
{username: 'user', password: 'pass'},
{
withCredentials: true, // 关键设置
headers: {'Content-Type': 'application/json'}
}
);
2. 服务器端配置
Node.js (Express)
const express = require('express');
const cors = require('cors');
const app = express();
// CORS 配置
const corsOptions = {
origin: 'https://your-frontend-domain.com', // 指定允许的源
credentials: true, // 允许发送凭证(Cookie)
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
};
app.use(cors(corsOptions));
// 设置 Cookie
app.post('/login', (req, res) => {
// 登录验证逻辑...
// 设置 Cookie(重要:SameSite=None; Secure)
res.cookie('sessionId', 'your-session-id', {
httpOnly: true,
secure: true, // HTTPS 必须
sameSite: 'none', // 跨域必须
maxAge: 24 * 60 * 60 * 1000, // 1天
domain: '.example.com' // 可选的域名设置
});
res.json({success: true});
});
Nginx 配置
server {
listen 80;
server_name api.example.com;
location / {
# CORS 配置
add_header 'Access-Control-Allow-Origin' 'https://your-frontend-domain.com';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With';
# 对于 OPTIONS 预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
proxy_pass http://backend;
}
}
3. Cookie 设置要点
关键属性
// 服务器设置 Cookie 时
Set-Cookie: sessionId=abc123;
HttpOnly; // 防止 JavaScript 访问
Secure; // 仅通过 HTTPS 传输(必须)
SameSite=None; // 允许跨站请求携带 Cookie(必须)
Max-Age=86400; // 过期时间
Domain=.example.com; // 指定域名
Path=/; // 路径
4. 完整示例代码
前端完整示例
<!DOCTYPE html>
<html>
<head>
<title>跨域登录示例</title>
</head>
<body>
<form id="loginForm">
<input type="text" id="username" placeholder="用户名">
<input type="password" id="password" placeholder="密码">
<button type="submit">登录</button>
</form>
<script>
document.getElementById('loginForm').addEventListener('submit', async (e) => {
e.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
try {
const response = await fetch('https://api.example.com/login', {
method: 'POST',
credentials: 'include', // 关键
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password })
});
if (response.ok) {
const data = await response.json();
console.log('登录成功:', data);
// 后续请求会自动携带 Cookie
} else {
console.error('登录失败');
}
} catch (error) {
console.error('请求错误:', error);
}
});
// 验证 Cookie 是否携带
async function testCookie() {
const response = await fetch('https://api.example.com/profile', {
credentials: 'include'
});
const data = await response.json();
console.log('用户信息:', data);
}
</script>
</body>
</html>
5. 常见问题排查
检查清单:
前端是否设置 credentials:
withCredentials: true (XMLHttpRequest)
credentials: 'include' (Fetch API)
服务器 CORS 配置:
- 是否设置了
Access-Control-Allow-Credentials: true
- 是否指定了具体的
Access-Control-Allow-Origin(不能为 *)
Cookie 属性:
- 是否有
Secure 属性(HTTPS 必须)
- 是否有
SameSite=None 属性
- 是否设置了正确的
Domain
HTTPS 要求:
- 本地开发:使用
http://localhost
- 生产环境:必须使用 HTTPS
预检请求处理:
调试方法:
// 查看请求头
console.log('请求头是否包含 Cookie:', document.cookie);
// 使用浏览器开发者工具查看:
// 1. Network 标签查看请求详情
// 2. Application > Cookies 查看存储的 Cookie
// 3. 检查 Console 中的 CORS 错误
6. 替代方案(如果 CORS 不可用)
JSONP(仅限 GET)
function handleLogin(data) {
console.log('登录结果:', data);
}
// 动态创建 script 标签
const script = document.createElement('script');
script.src = 'https://api.example.com/login?callback=handleLogin&user=xxx&pass=xxx';
document.body.appendChild(script);
代理服务器方案
// 前端请求自己的服务器
fetch('/api/proxy/login', {
method: 'POST',
body: JSON.stringify({username, password})
});
// 后端服务器代理转发
app.post('/api/proxy/login', (req, res) => {
const {username, password} = req.body;
// 转发到目标服务器
axios.post('https://api.example.com/login', {
username, password
}, {
withCredentials: true
}).then(response => {
res.json(response.data);
});
});
重要注意事项:
安全性:确保使用 HTTPS 保护 Cookie
SameSite 属性:Chrome 80+ 后,SameSite 默认为 Lax,跨域需要明确设为 None
测试环境:本地开发时,确保前后端域名不同以模拟真实跨域场景
Cookie 作用域:确保 Domain 设置正确,避免作用域问题
通过这些方法,你应该能够解决 Ajax 跨域请求未携带 Cookie 的问题。