通过代码,聊一下http请求中常会用到的cookie和session

Hello,大家好,今天跟大家聊一下客户端与服务器进行会话时,常用的技术cookie和session。

很多小伙伴,在面试的时候,都会背“cookie是保存在客户端的,session是保存在服务器端的……”

八股文谁都会背,但仅仅会背诵,却不理解,知识点很快就会忘记。今天,我们就结合代码来讨论一下这背后的原理到底是怎样的。

1、为什么要有cookie和session这些东西

Http,它是无状态协议,服务端无法感知到浏览器的登录状态。

所以,判断用户是否登录的任务就交给了服务器。

服务器则需要另外找寻字段来存放状态信息,然后根据这个字段,选择是否允许用户访问相应的内容。

因此,cookie诞生了。

2、Cookie

2.1 Cookie简介

cookie 中文叫作 “曲奇饼”,不太清楚为什么叫这个名字。

它在http的请求头和响应头中,都有对应的字段存储它。

  • 在请求头中是cookie
  • 在响应头中是set-cookie

如下图,是一个请求头,携带着的cookie字段。

在这里插入图片描述

2.2 Cookie是怎么用的

cookie可以理解为:保存在你电脑上的一段个人信息。

咱就以最简单的登录场景为例:

如果你第一次访问B站,那你肯定还没有进行过登录,因此,你访问的请求头里肯定也没有cookie字段。

当你登录成功之后,响应头会返回给你一个set-cookie字段,标识着服务器分配给你的cookie。(你可以开启无痕浏览模式,访问B站试一下)

在这里插入图片描述

在接收到这个请求之后,你的浏览器会将cookie保存下来。

然后,在下次浏览的时候。浏览器会在请求头中添加这个cookie,给到服务器。

接着,服务器会对你这个cookie,进行识别,来判断你的登录状态。

我这里用一段nodejs代码,演示一下:服务端处理的逻辑。

const express = require('express');
const app = express();
const cookieParser = require('cookie-parser');

app.use(cookieParser());

// 模拟用户数据库
const users = [
  { username: 'user1', password: 'pass1' },
];

app.post('/login', (req, res) => {
  const { username, password } = req.body;
  const user = users.find(u => u.username === username && u.password === password);
  if (user) {
    // 设置登录成功的 cookie
    res.cookie('loggedIn', true, { maxAge: 900000, httpOnly: true });
    res.send('Login successful');
  } else {
    res.send('Login failed');
  }
});

app.get('/protected', (req, res) => {
  // 检查登录 cookie
  if (req.cookies.loggedIn) {
    res.send('This is a protected resource.');
  } else {
    res.send('You need to log in to access this resource.');
  }
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

2.3 cookie存在不安全的问题

http携带着cookie,进行数据传输的时候,伴随着一定的风险。

万一这个请求被人劫持了咋办?

别人把http报文解析之后,你的个人信息username = zhangsan,不就完完全全地暴露出来了吗?

为了解决这个问题,session应运而生。

3、 Session是什么,以及怎么用

Session这个玩意,在我们日常使用的时候,就是一段ID。

这段ID,既不会暴露信息,又没有什么规律。

所以,它即便被别人劫持了,也不会出现信息泄露的风险。

Session一般有两种用法:

一个是放在url中,例如:某个站点是http://example.com,那么session数据可以作为url中的查询参数,放在其中:http://example.com/somepage?sessionId=123456

另外一个是放在请求头cookie中。没错,它需要放在http请求头,配合cookie字段一起使用。

针对第二种场景,用nodjs代码展示如下:

const express = require('express');
const session = require('express-session');
const mongoose = require('mongoose');
const MongoStore = require('connect-mongo');
const bcrypt = require('bcryptjs');

const app = express();

// 连接MongoDB
mongoose.connect('mongodb://localhost:27017/userAuth', {
  useNewUrlParser: true,
  useUnifiedTopology: true
});

// 用户模型
const UserSchema = new mongoose.Schema({
  username: String,
  password: String
});

const User = mongoose.model('User', UserSchema);

// 设置会话中间件
app.use(
  session({
    secret: 'your_secret_key',
    resave: false,
    saveUninitialized: true,
    store: MongoStore.create({ mongoUrl: 'mongodb://localhost:27017/mysession' }), // 使用MongoDB存储会话
    cookie: { secure: false } // 在HTTPS下设置为true
  })
);

// 注册新用户
app.post('/register', async (req, res) => {
  try {
    const hashedPassword = bcrypt.hashSync(req.body.password, 8);
    const user = new User({ username: req.body.username, password: hashedPassword });
    await user.save();
    res.send('用户注册成功');
  } catch (error) {
    res.status(500).send('用户注册失败');
  }
});

// 用户登录
app.post('/login', async (req, res) => {
  try {
    const user = await User.findOne({ username: req.body.username });
    if (user && bcrypt.compareSync(req.body.password, user.password)) {
      req.session.userId = user._id;
      res.send('登录成功');
    } else {
      res.status(401).send('用户名或密码错误');
    }
  } catch (error) {
    res.status(500).send('登录失败');
  }
});

// 用户登出
app.get('/logout', (req, res) => {
  req.session.destroy((err) => {
    if (err) {
      return res.send('会话销毁失败');
    }
    res.send('你已退出登录');
  });
});

// 受保护的路由
app.get('/protected', (req, res) => {
  if (req.session.userId) {
    // 验证用户ID是否存在于数据库中
    User.findById(req.session.userId, (err, user) => {
      if (err || !user) {
        return res.status(401).send('用户验证失败');
      }
      res.send('欢迎回来,这是受保护的页面!');
    });
  } else {
    res.status(401).send('请先登录');
  }
});

const PORT = 3000;
app.listen(PORT, () => {
  console.log(`服务器运行在 http://localhost:${PORT}`);
});

在这里,我们使用mongodb来存储用户数据。

用户在登录成功之后,便把mongodb中存放的用户ID(_id)数据放入set-cookie字段中,返回给浏览器。

请添加图片描述

当浏览器再次访问时,它将在http请求头的cookie字段中,携带这个sessionid,进行会话。

请添加图片描述

4、最后总结

从上面的案例中可以看到,在使用session的时候,我们其实是在使用_id进行登录状态验证的。

当服务端拿到_id之后,会去检查_id,是否存在于mongodb数据库中。如果存在,则证明用户是正常登录的,可以展示给他对应的用户数据。

而cookie则是直接使用保存在浏览器中的用户名进行比较,相对来说,安全性会更差一些。

这就是为什么八股文里会讲:cookie保存在客户端,session保存在服务端的原因了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

热爱技术和分享

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值