express-session + Redis做会话管理

使用express-session + Redis可以快速实现可靠的会话管理,并具有灵活性、扩展性和安全性等优点,适用于大型的Web应用程序。

const express = require('express');
const session = require('express-session');
const cookieParser = require('cookie-parser')
const { check, validationResult } = require('express-validator');
const passwordValidator = require('password-validator');
import RedisStore from "connect-redis"
import {createClient} from "redis"


const app = express();
const hostname = '127.0.0.1';
const port = 3000;

// Initialize client.
let redisClient = createClient()
redisClient.connect().catch(console.error)


// Initialize store.
let redisStore = new RedisStore({
    client: redisClient,
    prefix: "myapp:",
})
app.use(cookieParser());

// Initialize sesssion storage.
app.use(
    session({
        store: redisStore,
        resave: false, // required: force lightweight session keep alive (touch)
        saveUninitialized: false, // recommended: only save session when data exists
        secret: "keyboard cat",
        cookie: {
            httpOnly: true,//一个布尔值,表示是否将cookie标记为"HttpOnly",这样cookie就不能被JavaScript代码访问,从而防止了一些攻击方式。
            secure: false,//一个布尔值,表示是否将cookie标记为"Secure",这样cookie只能通过HTTPS协议发送,从而保证了安全性。
            maxAge: 1000 * 60 * 60 * 2 // 1 day
        }
    })
)
app.use(express.json()) // 这个中间件用于解析请求体中的JSON格式的数据
app.use(express.urlencoded({ extended: true })) //这个中间件用于解析请求体中的"application/x-www-form-urlencoded"格式的数据
function logParams(req, res, next) {
    console.log('Request parameters:', req.params);
    console.log('Request query:', req.query);
    console.log('Request body:', req.body);
    console.log("Request session:", req.session);
    next();
}

app.use(logParams);


// Initialize password validator.
const schema = new passwordValidator();
schema
    .is().min(8) // 长度不能小于8位
    .has().uppercase() //必须有大写字母
    .has().lowercase() // 必须有小写字母
    .has().digits() // 必须有数字
    .has().symbols() // 必须有符号
    .not().spaces(); // 不能有空格

app.post('/signup', [
    check('username').isLength({ min: 1 }).withMessage('用户名不能为空'),
    check('password').custom((value, {req}) => {
        if (!schema.validate(value)) {
            throw new Error('密码必须包含至少一个大写字母,一个小写字母,一个数字,一个特殊字符,且长度不少于8个字符');
        }
        return true;
    }),
], (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
        return res.status(422).json({ errors: errors.array() });
    }
    // ...
    // 处理用户注册逻辑
    // ...

    res.send('注册成功');
});

app.post('/login',
    [
        check('name').isLength({ min: 1 }).withMessage('姓名是必填项')
    ], (req, res) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(422).json({ errors: errors.array() });
        }
        // 登录成功后将用户信息存储在 session 中
        req.session.user = {id: 1, username: 'my-username'};
        res.send('登录成功');
    });
app.get('/logout', (req, res) => {
    req.session.destroy(err => {
        if (err) {
            console.error('Failed to destroy session:', err)
        } else {
            res.redirect('/login')
        }
    })
})
// 后续路由就可以取用户信息
app.get('/user', (req, res) => {
    // 从 session 中获取用户信息
    const user = req.session.user;

    if (!user) {
        return res.status(401).send('未登录');
    }
    res.send(`当前登录用户:${user.username}`);
});
// 启动应用程序
app.listen(port, hostname, () => {
    console.log(`Server running at http://``{hostname}:``{port}/`);
});

express-session 做会话有什么优缺点

优点:

  • 简单易用:express-session提供了一个简单的API,可以快速实现会话的管理和存储。
  • 可扩展性强:express-session可以与各种session存储引擎(如redis、mongodb、mysql等)集成,可以灵活地选择最适合自己应用的存储方式。
  • 安全性高:express-session自带的安全机制可以防止会话劫持和伪造,提高了应用程序的安全性。

缺点:

  • 存储开销:express-session的存储开销比较大,会占用一定的内存和磁盘空间,特别是在高并发的情况下,存储开销会更大。
  • 配置麻烦:如果需要使用session store,需要对session store的配置和使用进行详细的了解和配置,有一定的学习成本。
  • 不支持跨域:express-session不支持跨域访问,如果需要实现跨域访问的会话管理,需要进行额外的配置和处理。

综合来看,express-session是一个功能强大、可扩展性强、安全性高的会话管理中间件,但在使用时需要考虑存储开销和配置的复杂性。