Express项目Cookie不能保存在Application中

420 阅读4分钟

前言

最近准备在用NodeJs写一个关于鉴权登录的功能,相关库采用的是expressexpress-sessioncros。但是,在cookie保存问题上遇到了问题,即服务端生成的cookie无法保存到浏览器上。查询了好久,终于解决了这一问题,特来记录这一波折的经历.

1、express-session简介

express-sessionExpress的中间件,它将会话数据存储在服务器上;它仅仅将会话标识(sessionID)保存在cookie中,而非会话数据(用户名、密码等关键信息)。

2、express-session常用参数

参数作用
secret密钥,一个String类型的字符串,作为服务端的session签名
namecookie的名称,默认为connect.sid
resave每次访问之后,过期时间是否重新设置(建议为true)
saveUninitialized初始便生成cookie,但这个cookie无效
cookiecookie信息,默认值为{path:"/",httpOnly:true,secure:false,maxAge:null}
rolling在每次请求时强行设置cookie,这将重置cookie过期时间

3、express-session的使用

3.1、 首先安装express-session

npm i express-session

3.2、引入

const session = require("express-session")

3.3、配置

// 注册session中间件
app.use(session({
    name: "session-test",
    // 服务器生成的session签名
    secret: "shallow",
    // 每次访问之后,过期时间重新设置过期时间
    resave: true,
    // 最初访问网站就下发一个cookie,但是没有效果
    saveUninitialized:true,
    cookie:{
        // 过期时间 10min
        maxAge:1000 * 60 * 10,
    },
}))

3.4、生成cookie

初次尝试的代码如下

服务端代码:

// 创建应用对象
const express = require('express');
const session = require("express-session")
const bodyParser = require('body-parser');

const cors = require("cors")

// 创建应用对象
const app = express();

// 处理application/json内容格式的请求体
app.use(bodyParser.json());
// 处理application/x-www-form-urlencoded内容格式的请求体
app.use(bodyParser.urlencoded({extended: false}));

// 跨域配置
app.use(cors({
    // 允许任何源
    origin:"*"
}))

// 注册session中间件
app.use(session({
    name: "session-test",
    // 服务器生成的session签名
    secret: "shallow",
    // 每次访问之后,过期时间重新设置过期时间
    resave: true,
    // 最初访问网站就下发一个cookie,但是没有效果
    saveUninitialized:true,
    cookie:{
        // 过期时间 10min
        maxAge:1000 * 60 * 10,
        // domain:"127.0.0.1"
        // 为true时,表示只有https协议才能访问cookie
    },
}))

// 模拟数据库中存着的用户信息
var username = "test",
    password = "123";

app.get("/data", (req,res) =>{
    if(req.session.isLogin){
        res.send("已经登录,可访问数据")
    }else{
        res.send("请先登录")
    } 
})
// 创建路由规则
app.post("/login", (req,res) =>{
    if(req.body.username == username && req.body.password == password){
        req.session.isLogin = true
        res.send("login success")
    }
})
// 监听端口启动服务
app.listen(8002,() => {
    console.log("服务已启动,8002端口监听中...");
})

login.html代码如下:这里的ajax.js就是在上一篇文章中封装的XMLHttpRequest,详细代码链接:Express无法通过req.body获取请求传递的数据,本文的重点是Cookie不能保存在Application中,因此对封装的XMLHttpRequest就没有做多余的展示。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./ajax.js"></script>
</head>
<body>
    <button id="login">登录</button>
    <button id="getinfo">获取数据</button>
    <script>
        let dom = document.getElementById("login")
        let dom2 = document.getElementById("getinfo")
        
        // 登录请求
        dom.addEventListener("click",function(){
            let options = {
                method:"post",
                url:"http://127.0.0.1:8002/login",
                data:{"username":"test","password":"123"},
                contentType:"application/json",
                withCredentials: false
            }
            let res = AJAX(options)
            console.log("res", res);
        }) 
        
        //请求数据
        dom2.addEventListener("click",function(){
            let options = {
                method:"get",
                url:"http://127.0.0.1:8002/data",
                data:{"id": 1},
                withCredentials: true
            }
            let res = AJAX(options)
            console.log("res", res);
        })
    </script>
</body>
</html>

点击登录按钮后,我们可以看到,登录请求成功,服务端成功返回数据res login success,并且cookie中有我们在espress-session中配置的namesession-test,如下图所示:

image.png

服务端也成功生成session,并设置isLogin值为Truereq.session.isLogin = true)。

image.png

但是!!!,Application中的cookie空空如也。

image.png

4、解决方法

在网上搜寻了各种资料之后,得出,要想浏览器保存cookie,有一个前提 就是跨域的设置。要设置Access-Control-Allow-Origincredentials

需要注意的一点是,此时Access-Control-Allow-Origin不能像之前那样,设置为*了,必须要指定具体的域名。否则,就算是后台可以返回数据了,但是origin不同域,浏览器依旧不会去保存cookie

同时也要将credentials设置为true,告知前端可以携带cookie请求头。同时前端要在请求时,也要将withCredentials设置为true

// 跨域配置
app.use(cors({ 
    origin:"http://127.0.0.1:5500",
    credentials: true, 
}))

前端发请求的设置,注意,这里的withCredentials并不是在里setRequestHeader里设置。(xhr.setRequestHeader("withCredentials"), true)

而是通过xhr.withCredentials = true来设置。

login.html代码如下:(修改部分)

dom.addEventListener("click",function(){
    let options = {
        method:"post",
        url:"http://127.0.0.1:8002/login",
        data:{"username":"test","password":"123"},
        contentType:"application/json",
        // 添加了withCredentials
        withCredentials: true
    }
    let res = AJAX(options)
    console.log("res", res);
}) 

经过以上设置,我们点击登录按钮之后,成功将cookie设置到浏览器中。

image.png

5、总结

如果想将Cookie保存在Application中,需要明确以下四个点:

  1. 服务端的跨域配置项Access-Control-Allow-Origin不能设置为*,需要设置为对应的域名。
  2. 服务端Access-Control-Allow-Credentials设置为true,允许前端携带cookie信息。
  3. 请求时,将withCredentials设置为true