Express+Vue+axios在前后端分离的项目中使用Session

Express+Vue+axios在前后端分离的项目中使用Session

  • 前言
  • Session简介
  • 搭建带有Session功能的后端服务器
  • 搭建前端服务器
  • 跨域资源共享问题
  • 在axios中传cookie
  • 小结
  • 源代码

前言

当今流行的网页开发模式是前后端分离的开发模式,即后端只负责API的开发,将其暴露给前端,前端负责页面逻辑,利用后端的API完成网页的构建。而在开发大型应用时,session又是常用的用来保存信息的技术。

这篇文章在后端使用Node.js的Express框架,前端使用Vue.js,用axios进行异步请求,介绍如何在前后端分离的项目中使用Session。

注意:前后端分离可以有两种实践方案:

  • 将前端工程和后端工程分别部署在不同的服务器上,此时前端工程相当于是一个静态页面,通过调用后端的API完成页面交互。
  • 将前端工程作为静态文件部分和后端工程部署在同一个服务器上,交互的逻辑与第一种相同。

这篇文章采用第一种方案。

Session简介

Session存储信息和Cookie存储信息类似,不过传统的Cookie存储方法是将信息存储在客户端(浏览器),而Session是将信息存储在服务器上。

Session也是依赖Cookie的!

在浏览器中,一个Session Key被存储在Cookie中,这是一个应当被精心设计过的字符串,它应当独一无二,而且还要避免被暴力破解。这个Session Key是用户身份的识别信息,一个Session Key对应一个用户。

在服务器中存储着很多的Session,中文成为会话,它们就像服务器和客户端的会话一样,一种常见的实现就是哈希表,它的键是Session Key,值就是存储的信息,服务器根据用户的Session Key决定给用户访问特定信息的权限。

当用户发送请求到服务器时,Session Key在Cookie字段中也被发送给服务器,这时服务器根据Session Key识别用户,验证通过后就会将该Session Key对应的数据返回给用户。

例如当一个用户登录时,验证成功后一个Session Key会被保存在浏览器内,当用户关闭浏览器一段时间后再打开,在请求网页时,会将这个Session Key发给服务器,服务器收到后发现用户已经登录,且登录还没有过期,就会直接让用户登录,不用再进行校验。

搭建带有Session功能的后端服务器

后端采用Node.js的Express框架,首先要安装最新版本的Node,这里不赘述了,然后要安装Express和Session,这是两个库,要分别安装:

npm install --save express
npm install --save express-session

新建一个JS文件,命名为app.js,这是我们这个后端的主入口,建立后文件结构应该如下:
Express+Vue+axios在前后端分离的项目中使用Session_第1张图片

app.js中添加如下内容:

const express = require('express');
const session = require('express-session');

const app = express();
app.use(session({
    secret: 'billy',
    resave: false,
    saveUninitialized: false,
}));

app.listen(3000);
  • 前两行代码引入了expressexpress-session两个库。
  • 第4行的代码创建了一个express实例,通过这个实例我们才可以创建API。
  • 第5-9行代码配置了Session。secret选项是生成Session Key的哈希函数使用的密钥,相当于在生成Session Key时哈希函数的输入值多了一些内容,有助于避免结果被破解。resave选项表示是否重复保存,有时用户访问Session后并没有修改Session的内容,这时候如果把这个选项设置成false可以避免在Session未被修改时重复保存。saveUninitialized表示是否保存未初始化的Session,有时用户访问页面时并没有创建Session,比如用户进了主页但是并没有登录或注册,这时候Session是没有初始化的,把这个选项设置为false可以避免由于用户并未使用Session而在服务器保存多余的Session。
  • 最后一行代码监听3000端口,表明服务器运行在本机的3000端口上。

Express中的Session默认将Session保存在内存中,也就是说,如果后端服务器重启,所有的Session数据都将丢失,在实际开发中,如果你需要长期保存Session的数据,请务必使用数据库来保存Session。

让我们来配置一个路由,我们在Session中保存一个用户最早访问网站的时间,如果用户是第一次访问网站,我们就保存下当前的时间并返回这个时间,如果用户此前已经访问过了我们的网站,就返回用户最早访问这个网站的时间。

在express中,session被保存在req.session中,这是一个JavaScript对象,在服务端可以对这个对象进行增删改查操作,上文说过了,不同的用户的Session也是不同的,所以req.session根据用户不同访问到的是不同的对象,完成后我们的服务器端的app.js文件如下:

const express = require('express');
const session = require('express-session');

const app = express();
app.use(session({
    secret: 'billy',
    resave: false,
    saveUninitialized: false,
}));

app.get('/time', (req, res) => {
    if (!req.session.time) {
        req.session.time = Date();
    }
    res.send(req.session.time);
});

app.listen(3000);

启动后端服务器:

node app.js

可以在浏览器中访问localhost:3000/time看看效果如何:

Express+Vue+axios在前后端分离的项目中使用Session_第2张图片

刷新后或者关闭浏览器重新打开都不会改变这个时间,因为它已经被保存在Session中了,根据我们的逻辑它是不会改变的。

搭建前端服务器

我们用Vue CLI 快速搭建一个前端页面,如果你想了解一下Vue CLI如何快速搭建项目,可以参考我的另外一篇文章:Vue CLI 3 快速搭建项目。

我们使用axios这个库进行异步请求的发送,这是一个基于Promise的异步库,如果你想了解更多关于axios的内容,请移步axios 中文说明。

安装axios

npm install --save axios

为了方便我把全部的内容都放在App.vue这个文件中了,文件内容如下:

<template>
    <div id="app">
        <h1>Time: {{time}}h1>
    div>
template>

<script>
    import axios from 'axios';

    export default {
        name: 'app',
        data() {
            return {
                time: null,
            }
        },
        created() {
            axios.get('http://localhost:3000/time')
                .then((res) => {
                    this.time = res.data;
                });
        }
    };
script>

这个页面的逻辑就是当页面加载时,异步从服务器获取最早访问的时间,然后将这个时间字符串赋值给data中的time属性,在页面上将这个字符串渲染出来。

跨域资源共享问题

以上页面逻辑应该是毫无问题的,但是启动前端项目打开页面后产生如下结果:

Express+Vue+axios在前后端分离的项目中使用Session_第3张图片

什么内容都没显示出来,打开浏览器终端看一下报错:
Express+Vue+axios在前后端分离的项目中使用Session_第4张图片

这里涉及到了CORS,中文全称为跨域资源共享的问题,是一个浏览器端的保护措施。浏览器为了保护用户的安全,在正常情况下会禁止从其他域读取资源。什么情况下浏览器会认为是不同的域呢?有的人可能在这里会产生疑惑,明明都是localhost域,为什么还会有跨域请求的问题呢?

其实,只有协议域名端口都相同时,浏览器才会认为是同源的,这里协议和域名是相同的,但是端口不同(后端是3000端口,前端是8080端口),所以浏览器仍然认为这是一个跨域请求,出于安全考虑禁止了它。

为了允许跨域请求,需要在后端添加响应头,字段名为Access-Control-Allow-Origin,我们在后端用一个中间件来进行配置,在app.js中添加如下内容:

app.use((req, res, next) => {
    res.set('Access-Control-Allow-Origin', '*');
    next();
});

这里的*就是允许跨域访问,而且允许的域不限。

重启应用后打开网页,可以正常显示内容了:
Express+Vue+axios在前后端分离的项目中使用Session_第5张图片

在axios中传cookie

虽然数据能正常显示,但是新问题又出现了,正常来说页面刷新以后时间应当是不变的,因为服务器会一直返回同一个Session中保存的数据,但是这里刷新后时间会不停变化,也就是说,每次访问接口,服务器都会新建一个Session,这不是我们想要的结果,打开浏览器终端,切到Network标签查看一下请求内容如下:

Express+Vue+axios在前后端分离的项目中使用Session_第6张图片

我们在这个请求中可以发现,没有任何关于Cookie的内容,也就是说,Session Key没有传给服务器,在这种情况下,服务器每次都以为是一个新用户连接进来了,所以返回了新的Session中的内容,为了解决这个问题,我们要让axios把Cookie传给服务器,在App.vue中引入axios的代码下添加如下代码:

axios.defaults.withCredentials = true;

添加后打开网页,又无法正常显示了,打开终端看一下报错:
Express+Vue+axios在前后端分离的项目中使用Session_第7张图片
由于设置完毕后,请求中包含了如Cookie等私密内容,所以允许跨域的设置中不能将允许的域设置为全部(*),而且后端还要特别指明允许私密内容的发送,我们这里要在后端进行一些修改:

app.use((req, res, next) => {
    res.set('Access-Control-Allow-Origin', 'http://localhost:8080');
    res.set('Access-Control-Allow-Credentials', 'true');
    next();
});

修改完成后内容可以正常显示了,且刷新后不会再变化,说明访问的是同一个Session:
最终结果

最终的请求也已经包含了Session Key
Express+Vue+axios在前后端分离的项目中使用Session_第8张图片
最终前端代码:

<template>
    <div id="app">
        <h1>Time: {{time}}h1>
    div>
template>

<script>
    import axios from 'axios';
    axios.defaults.withCredentials = true;

    export default {
        name: 'app',
        data() {
            return {
                time: null,
            }
        },
        created() {
            axios.get('http://localhost:3000/time')
                .then((res) => {
                    this.time = res.data;
                });
        }
    };
script>

<style>

style>

最终后端代码:

const express = require('express');
const session = require('express-session');

const app = express();
app.use((req, res, next) => {
    res.set('Access-Control-Allow-Origin', 'http://localhost:8080');
    res.set('Access-Control-Allow-Credentials', 'true');
    next();
});

app.use(session({
    secret: 'billy',
    resave: false,
    saveUninitialized: false,
}));

app.get('/time', (req, res) => {
    if (!req.session.time) {
        req.session.time = Date();
    }
    res.send(req.session.time);
});

app.listen(3000);

小结

  • 前后端分离的项目中要注意跨域资源获取问题,通过Access-Control-Allow-Origin设置,如果要传Session Key的话还要再允许私密信息的传输,通过Access-Control-Allow-Credentials设置。
  • axios默认是不会传输Cookie等信息的,要手动配置才可以,通过axios.defaults.withCredentials = true来设置。

源代码

本文中的例子我已经放到GitHub上,请点击访问前端代码或后端代码来查看或下载它们。

你可能感兴趣的:(JavaScript,Vue.js,Node.js)