使用redis+cookie做单点登录(node+express)

单点登录的实现方式有很多种,在这里博主就先使用了简单的cookie+redis做了一个单点登录
注:没有绝对的安全,所以我们加强验证,增加攻击者攻破的难度

我的思路是这样的
1:首先用户输入账号密码登录后在数据库进行对比
2:账号密码错误重新登录,账号密码正确则进行下一步
3:cookie在同一个浏览器中是共享的,在用户登录成功之后,我们使用加密算法进行加密混淆形成一个token存入cookoie中,键为自定义标识字段(比如userSSH),值为token(不要在cookie中存储重要信息,容易被破解,所以在这里我们存入token,这个token就是验证用户信息的媒介
4:cookie已经形成了,这时候用到了redis,就好比一个验证用户中心数据库一样,我们将形成的cookie的值,就是这个token存入redis中,键为token,值为用户的信息,并设置过期销毁的时间
5:在这里我们已经形成了cookie和redis用户信息,用户使用其他域名访问的时候,首先验证是否有userSSH这个cookie,如果没有,则引导至登录界面
6:如果有这个cookie,则把这个cookie的值token取出,用这个凭证去和redis中的信息对比,凭证生效,用户可以正常操作,并刷新redis中该数据的过期时间,凭证无效,则引导至登录界面
7:至此,redis+cookie实现的单点登录算是完成了,但在实际应用中一定远远比这个复杂,尤其是安全考虑方面

前端代码:简单的form表单

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <div style="border:3px solid green;width: 500px;height: 500px;">
        登录界面
        <form action="http://localhost:3000/userLogin" method="POST">
            用户名:<input type="username" value="" name="username">
            密码:<input type="password" value="" name="password">
            <button type="submit">提交</button>
        </form>
    </div>
</body>

</html>

服务端工程目录结构:
在这里插入图片描述
3000端口:

const express = require('express');
const app = express();
const mongoose = require('mongoose');
const mongoIndex = require('../mongodb-config/index');
const token = require('../token-config/userToken');
const typeData = require('../baseData/typeData');
const session = require('express-session');
const cookie = require('cookie-parser');
const redis = require('../redis-config/redis-config')
app.use(cookie());
//session/cookie中间件
app.use(session({
  secret: 'zzw',//对session id相关的cookie进行签名
  resave: false,
  saveUninitialized: false,//是否保存未初始化的的会话
  rolling: true,
  cookie: {
    maxAge: 1000 * 60 * 3//设置session存活时间,单位毫秒
  }
}));

/**
 * 
 */
/* GET home page. */
app.get('/index', async (req, res, next) => {
  console.log(req.cookies);
  if (await typeData.typeData(req.session.userSSH)) {
    console.log('用户的session存在,可以访问');
    res.redirect('/enter');
  } else if (await typeData.typeData(req.cookies.userSSH)) {
    console.log('用户的cookie存在,需要验证');
    res.redirect('/enterToken');
  } else {
    console.log('用户的cookie不存在,需要登录');
    res.redirect('../login.html');
  }
});
app.post('/userLogin', async (req, res, next) => {
  const user = await mongoIndex.mongoUser.find(
    {
      username: req.body.username,
      password: req.body.password
    },
    '_id username password',
  ).catch((err) => {
    if (err) {
      res.send('error');
    }
  });

  if (await typeData.typeData(user)) {//判断是否有效
    let userResult = {
      username: user[0].username,
      password: user[0].password
    };
    res.cookie('userSSH', token);//将用户信息存在cookie中(键为标识字段,值为token)
    await mongoIndex.mongoUser.updateOne(
      {
        _id: mongoose.Types.ObjectId(user[0]._id)
      },
      {
        $set:
        {
          user_ssh: token
        }
      }
    );
    redis.set(token, JSON.stringify(userResult));//将用户的账号密码转换存入redis(键为token,值为用户信息)
    redis.PEXPIRE(token, 1000 * 60 * 3);//设置过期时间并删除
    redis.get(token, (err, data) => {
      console.log(JSON.parse(data));
    });
    //存入redis数据库中用于数据共享
    res.redirect('/enter');
  } else {
    res.redirect('../login.html');
  }
});
//cookie认证中心
app.get('/enterToken', async (req, res, next) => {  
  redis.get(req.cookies.userSSH, async (err, data) => {
    if (await typeData.typeData(data)) {//如果该cookie的凭证有效
      req.session.userSSH = req.cookies.userSSH;//则重置sesssion
      redis.PEXPIRE(req.session.userSSH, 1000 * 60 * 3);//重新设置过期时间
      res.redirect('/enter');
    } else {
      console.log('凭证无效,session和redis已到期,需要登录');
      res.redirect('../login.html');//如果该cookie凭证无效,则引导用户登录
    }
  });
});
app.get('/enter', (req, res, next) => {
  res.send('用户的cookie认证成功,已生成session,可以正常访问');
});
module.exports = app;

redis配置:

const redis = require('redis');
const redisServer = redis.createClient('6379', '127.0.0.1');

redisServer.on('connect', () => {
    console.log('redis连接成功',);
});
redisServer.on('error', () => {
    console.log('redis连接异常');
});
module.exports = redisServer;

mongodb配置:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/user_test', { useNewUrlParser: true, poolSize: 5 });

//判断连接是否生效
const conn = mongoose.connection;

conn.on("open", () => {
    console.log('mongodb连接成功');
})
conn.on("error", () => {
    console.log('mongodb连接失败');
});

mongoose骨架配置:

const mongoose = require('mongoose');

//需要生成可以操作表数据的对象和Schema
const user = mongoose.Schema({
    username: { type: String },
    password: { type: String },
    user_ssh: { type: String }
}, { collection: "user", versionKey: false, strict: true });

module.exports = mongoose.model('user', user);



token配置:

const crypto = require('crypto');

/**
 * 生成令牌和token
 * @return {string} return 返回值
 */
function getToken(){
    let buf = crypto.randomBytes(12);
    let token = buf.toString('hex');
    return token;
}
module.exports = getToken();

基础验证配置:

class typeData {

async typeData(data) {
    if (data == '' ||
        data == null ||
        data == false ||
        data == undefined ||
        data == [] ||
        data == {}) {
        return false;
    } else {
        return true;
    }
}

};
module.exports = new typeData();

app.js的配置(增加了mongodb和redis的初始化)

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
const mongo = require('./mongodb-config/mongoConfig');
const redis = require('./redis-config/redis-config')
var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

另一个测试借口:3100

const express = require('express');
//const session = require('express-session');
const cookie = require('cookie-parser');
const app = express();
const redis = require('redis');
const redisServer = redis.createClient('6379', '127.0.0.1');
const typeData = require('./baseData/typeData');
redisServer.on('connect', () => {
  console.log('redis连接成功');
});
redisServer.on('error', () => {
  console.log('redis连接异常');
});
app.use(cookie());
//session/cookie中间件
/*app.use(session({
  secret: 'zzw',//对session id相关的cookie进行签名
  resave: false,
  saveUninitialized: false,//是否保存未初始化的的会话
  rolling: true,
  cookie: {
    maxAge: 1000 * 60 * 3//设置session存活时间,单位毫秒
  }
}));*/

//先判断cookie是否存在
app.get('/index', async (req, res, next) => {
  if (await typeData.typeData(req.cookies.userSSH)) {
    res.redirect('/cookieSuccess?cookieData=' + req.cookies.userSSH);
  } else {
    res.redirect('/cookieErr?cookieData=' + req.cookies.userSSH);
  }
});
app.get('/cookieErr', (req, res, next) => {
  res.send('用户的cookie标识不存在!!');
  //用户的cookie标识不存在证明用户没有登录,此时可以引导至登录界面
});
//在用这个凭证和redis中的凭证进行对比
app.get('/cookieSuccess', (req, res, next) => {
  redisServer.get(req.query.cookieData, (err, data) => {
    console.log('data  = ', data);
    res.send({ data: data, token: req.query.cookieData });
  });
})
app.listen(3100);


猜你喜欢

转载自blog.csdn.net/qq_42427109/article/details/86677224