Экспресс не устанавливает cookie
У меня проблема с настройкой файлов cookie через express. Я использую Este.js dev stack
и я пытаюсь установить cookie в API auth /login
route. Вот код, который я использую в /api/v1/auth/login
route
res.cookie('token', jwt.token, {expires: new Date(Date.now() + 9999999)});
res.status(200).send({user, token: jwt.token});
В src/server/main.js
я зарегистрировал cookie-parser
как первое промежуточное программное обеспечение
app.use(cookieParser());
Заголовок ответа для маршрута /api/v1/auth/login
содержит
Set-Cookie:token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ..
но cookie не сохраняется в браузере (document.cookie
пуст, а также Resources - Cookies
вкладка Resources - Cookies
в инструментах develepoers пуста) :(
EDIT: Я обнаружил, что когда я вызываю это в /api/v1/auth/login
(без вызова res.send
или res.json
)
res.cookie('token', jwt.token, {expires: new Date(Date.now() + 9999999), httpOnly: false});
next();
то cookie установлен. И заголовок ответа имеет X-Powered-By:Este.js
... это устанавливает esteMiddleware
в esteMiddleware
frontend esteMiddleware
part.
Когда я использую res.send
res.cookie('token', jwt.token, {expires: new Date(Date.now() + 9999999), httpOnly: false}).send({user, token: jwt.token});'
next();
то я получаю ошибку Can't set headers after they are sent.
потому что используется метод send
, поэтому frontend render throw this error.
Но я должен отправить данные из API, так как я могу справиться с этим?
Может кто-нибудь мне помочь? Благодарю!
Ответы
Ответ 1
Я была такая же проблема. Ответ сервера поставляется с набором файлов cookie:
Set-Cookie:my_cookie=HelloWorld; Path=/; Expires=Wed, 15 Mar 2017 15:59:59 GMT
Но cookie не был сохранен браузером.
Вот как я это решил.
Я использую fetch
в клиентском коде. Если вы не укажете credentials: 'include'
в параметрах fetch
, файлы cookie не отправляются на сервер и не сохраняются браузером, хотя ответ сервера устанавливает куки.
Пример:
var headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
return fetch('/your/server_endpoint', {
method: 'POST',
mode: 'same-origin',
redirect: 'follow',
credentials: 'include', // Don't forget to specify this if you need cookies
headers: headers,
body: JSON.stringify({
first_name: 'John',
last_name: 'Doe'
})
})
Надеюсь, это поможет кому-то.
Ответ 2
я работаю с выражением 4 и узлом 7.4 и угловым, у меня была та же проблема, я помогаю этому:
a) серверная сторона: в файле app.js я даю заголовки всем ответам вроде:
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
это должно быть до всех маршрутизаторов.
Я видел много добавленных заголовков:
res.header("Access-Control-Allow-Headers","*");
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
но мне это не нужно,
б) когда вы определяете cookie, вы добавляете httpOnly: false, например:
res.cookie( key, value,{ maxAge: 1000 * 60 * 10, httpOnly: false });
c) клиентская сторона: в send ajax вам нужно добавить: "withCredentials: true", например:
$http({
method: 'POST',
url: 'url,
withCredentials: true,
data : {}
}).then(function(response){
// code
}, function (response) {
// code
});
удачи.
Ответ 3
Есть несколько вопросов:
- cookie, который явно не задан с помощью
httpOnly: false
не будет доступен через document.cookie
в браузере. Он по-прежнему будет отправляться с HTTP-запросами, и если вы проверите средства разработки браузеров, вы, скорее всего, найдете там cookie (в Chrome их можно найти на вкладке "Ресурсы" инструментов dev); -
next()
который вы вызываете, должен использоваться только в том случае, если вы хотите отложить отправку ответа на какую-либо другую часть вашего приложения, что, судя по вашему коду, - это не то, что вы хотите.
Итак, мне кажется, что это должно решить ваши проблемы:
res.cookie('token', jwt.token, {
expires : new Date(Date.now() + 9999999),
httpOnly : false
});
res.status(200).send({ user, token: jwt.token });
В качестве побочного примечания: существует причина, по которой httpOnly
не соответствует true
(чтобы не допустить, чтобы злонамеренные скрипты XSS httpOnly
к httpOnly
сеансов и тому подобное). Если у вас нет оснований для доступа к файлу cookie через JS на стороне клиента, не устанавливайте его в false
.
Ответ 4
Дважды проверьте размер вашего куки.
Для меня способ, которым я генерировал токен аутентификации для хранения в моем cookie файле, вызывал увеличение размера cookie файла с последующими попытками входа в систему, в результате чего браузер не устанавливал cookie файл из-за его слишком большого размера.
Шпаргалка размера печенья браузера
Ответ 5
app.post('/api/user/login',(req,res)=>{
User.findOne({'email':req.body.email},(err,user)=>{
if(!user) res.json({message: 'Auth failed, user not found'})
user.comparePassword(req.body.password,(err,isMatch)=>{
if(err) throw err;
if(!isMatch) return res.status(400).json({
message:'Wrong password'
});
user.generateToken((err,user)=>{
if(err) return res.status(400).send(err);
res.cookie('auth',user.token).send('ok')
})
})
})
});
Ответ 6
Одна из главных особенностей - правильно настроить заголовок.
Для nginx:
add-header Access-Control-Allow-Origin '' domain.com ';
add_header 'Access-Control-Allow-Credentials' 'true';
Добавьте это на свой веб-сервер.
Затем создайте файл cookie следующим образом:
"cookie": {
"secure": true,
"path": "/",
"httpOnly": true,
"hostOnly": true,
"sameSite": false,
"domain" : "domain.com"
}
Лучший способ получить куки файл из экспресса - использовать cookie-parser.
Ответ 7
Файл cookie не может быть установлен, если клиент и сервер находятся в разных доменах. Разные субдомены возможны, но не разные домены и не разные порты.
Если вы используете Angular в качестве внешнего интерфейса, вы можете просто отправить все запросы в тот же домен, что и ваше приложение Angular (таким образом, приложение отправляет все запросы API к себе), и вставить/api/в каждый URL-адрес HTTP-запроса HTTP - обычно это настраивается в вашей среде..ts файл:
export const environment = {
production: false,
httpPhp: 'http://localhost:4200/api'
}
Тогда все HTTP-запросы будут использовать environment.httpPhp + '/rest/of/path'
Затем вы можете прокси эти запросы, создав proxy.conf.json следующим образом:
{
"/api/*": {
"target": "http://localhost:5200",
"secure": false,
"changeOrigin": true,
"pathRewrite": {
"^/api": ""
}
}
}
Затем добавьте это в ng serve:
ng serve -o --proxy-config proxy.conf.json
Затем перезапустите ваше приложение, и все должно работать, при условии, что ваш сервер фактически использует Set-Cookie в заголовках ответа HTTP. (Обратите внимание, что в домене diff вы даже не увидите заголовок ответа Set-Cookie, даже если сервер настроен правильно).