Express未设置Cookie

时间:2016-04-24 14:01:49

标签: node.js express cookies

我有通过快递设置cookie的问题。我正在使用Este.js dev stack,我尝试在API auth /login路由中设置Cookie。以下是我在/api/v1/auth/login路径

中使用的代码
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标签为空):(

修改 我发现,当我在/api/v1/auth/login(没有通话res.sendres.json

中拨打此电话时

res.cookie('token', jwt.token, {expires: new Date(Date.now() + 9999999), httpOnly: false}); next();

然后设置了Cookie 并且响应标头设置了X-Powered-By:Este.js ...这会在expres frontend rendering part中设置esteMiddleware

当我使用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方法,所以前端渲染会抛出此错误。

但我必须从API发送数据,那么我该如何处理呢?

请帮帮我一下吗?谢谢!

12 个答案:

答案 0 :(得分:26)

我有同样的问题。服务器响应带有cookie集:

Set-Cookie:my_cookie=HelloWorld; Path=/; Expires=Wed, 15 Mar 2017 15:59:59 GMT 

但是浏览器没有保存cookie。

这就是我解决它的方法。

我在客户端代码中使用fetch。如果您未在credentials: 'include'选项中指定fetch,则虽然服务器响应设置了Cookie,但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'
    })
})

希望它对某人有帮助。

答案 1 :(得分:4)

为此苦苦挣扎了3个小时,终于意识到,即使我只收到Cookie,我也应该将axios设置为withCredentials

true

答案 2 :(得分:3)

我使用快递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');

但我不需要那个,

b)当你定义cookie你需要添加httpOnly:false,如:

 res.cookie( key, value,{ maxAge: 1000 * 60 * 10, httpOnly: false });

c)客户端:在发送ajax时你需要添加:" withCredentials:true,"像:

$http({
     method: 'POST',
     url: 'url, 
     withCredentials: true,
     data : {}
   }).then(function(response){
        // code  
   }, function (response) {
         // code 
   });
祝你好运。

答案 3 :(得分:2)

有一些问题:

  • 未使用httpOnly : false明确设置的Cookie将可通过浏览器中的document.cookie访问。它仍将与HTTP请求一起发送,如果您检查浏览器的开发工具,您很可能会在那里找到cookie(在Chrome中,它们可以在开发工具的 Resources 标签中找到);
  • 您呼叫的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脚本访问会话cookie等)是有原因的。如果您没有充分的理由通过客户端JS访问cookie,请不要将其设置为false

答案 4 :(得分:1)

仔细检查您的cookie的大小。

对我来说,我生成身份验证令牌以存储在cookie中的方式,导致cookie的大小随随后的登录尝试而增加,最终导致浏览器未设置cookie,因为它太大了。

Browser cookie size cheat sheet

答案 5 :(得分:1)

在cookie中将“ httpOnly”设置为true是没有问题的。

我正在对请求使用“ request-promise”,客户端是“ React”应用程序,但是技术并不重要。该请求是:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.binary_location = '/path/to/chrome'
driver = webdriver.Chrome(options=options, executable_path='/path/to/chromedriver')
driver.get('http://google.com/')

node.js(快速)服务器上的响应是:

    var options = {
        uri: 'http://localhost:4000/some-route',
        method: 'POST',
        withCredentials: true
    }

    request(options)
        .then(function (response) {
            console.log(response)
        })
        .catch(function (err) {
            console.log(err)
        });

客户端发出请求,服务器设置cookie,浏览器(客户端)接收它(您可以在“开发工具”的“应用程序”选项卡中看到它),然后我再次向服务器和cookie启动请求位于请求:“ req.headers.cookie”中,因此服务器可以进行访问以进行验证。

答案 6 :(得分:1)

我对跨源请求有同样的问题,这是我如何解决的。您需要专门告诉浏览器允许凭据。使用axios,您可以指定它以允许在每个请求上使用凭据,例如 axios.defaults.withCredentials = true 但是,这将被CORS政策阻止,您需要在api上指定凭据为真

const corsOptions = {
    credentials: true,
    ///..other options
  };

app.use(cors(corsOptions));

更新:这仅适用于本地主机 有关生产环境中问题的详细答案,请参见我的答案here

答案 7 :(得分:1)

我在 Angular 应用程序中遇到了同样的问题。尽管我使用过,但浏览器中并未设置 cookie

res.cookie("auth", token, {
  httpOnly: true,
  sameSite: true,
  signed: true,
  maxAge: 24 * 60 * 60 * 1000,
});

为了解决这个问题,我在服务器端的app.use(cors({ origin:true, credentials:true }));文件中添加了app.js

在我的 Angular 客户端的订单服务中,我添加了 {withCredentials: true} 作为第二个参数,当 http 方法被调用时,就像下面的代码

getMyOrders() {
return this.http
  .get<IOrderResponse[]>(this.SERVER_URL + '/orders/user/my-orders', {withCredentials: true})
  .toPromise();}

希望对大家有所帮助。

答案 8 :(得分:0)

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')
            })
        }) 
    })
});

响应

res.cookie('auth',user.token).send('ok')

服务器响应正常,但cookie未存储在浏览器中

解决方案:

在chrome中添加Postman Interceptor Extension,使邮递员可以将cookie存储在浏览器中并重新使用请求。

答案 9 :(得分:0)

我也遇到了同样的问题。

代码在两个地方发生了变化:

  1. 在客户端:
   const apiData = await fetch("http://localhost:5000/user/login", 
{
        method: "POST",
        body: JSON.stringify(this.state),
        credentials: "include", // added this part
        headers: {
          "Content-Type": "application/json",
        },
      })
  1. 在后端:
   const corsOptions = {
    origin: true, //included origin as true
    credentials: true, //included credentials as true
};

app.use(cors(corsOptions));

答案 10 :(得分:-1)

主要功能之一是正确设置标题。

对于nginx:

add-header Access-Control-Allow-Origin''domain.com';

add_header'Access-Control-Allow-Credentials''true';

将此添加到您的Web服务器。

然后使用如下形式的cookie:

"cookie": {
        "secure": true, 
        "path": "/", 
        "httpOnly": true, 
        "hostOnly": true, 
        "sameSite": false, 
        "domain" : "domain.com"
    }

从express获取cookie的最佳方法是使用cookie解析器。

答案 11 :(得分:-1)

如果客户端和服务器位于不同的域,则无法设置Cookie。可以使用不同的子域,但是不能使用不同的域和端口。

如果使用Angular作为前端,您可以将所有请求发送到与Angular应用程序相同的域(因此该应用程序会将所有API请求发送给自己),然后在每个HTTP API请求URL中粘贴/ api /-通常已配置在您的environment.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服务:

ng serve -o --proxy-config proxy.conf.json

然后重新启动应用程序,它应该可以正常工作,假设您的服务器实际上在HTTP响应标头中使用Set-Cookie。 (请注意,在diff域上,即使服务器配置正确,您也看不到Set-Cookie响应标头。)