Session验证用户登录的大致过程

 

1. 用户提交包含用户名和密码的表单,发送HTTP请求。

2. 服务器验证用户发来的用户名密码。

3. 如果正确则把当前用户名(通常是用户对象)存储到redis中,并生成它在redis中的ID。 这个ID称为Session ID,通过Session ID可以从Redis中取出对应的用户对象, 敏感数据(比如authed=true)都存储在这个用户对象中。

4. 设置Cookie为sessionId=xxxxxx|checksum并发送HTTP响应, 仍然为每一项Cookie都设置签名。

 

 signedCookie

计算机领域有个名词叫 签名,专业点说,叫 信息摘要算法。

比如,我们现在面临着一个菜鸟开发的网站,他用 cookie 来记录登陆的用户凭证。相应的 cookie 长这样:dotcom_user=alsotang,它说明现在的用户是 alsotang 这个用户。如果我在浏览器中装个插件,把它改成dotcom_user=ricardo,服务器一读取,就会误认为我是 ricardo。然后我就可以进行 ricardo 才能进行的操作了。

之前 web 开发不成熟的时候,用这招甚至可以黑个网站下来,把 cookie 改成 dotcom_user=admin 就行了。

现在是怎么保证不被篡改呢?答案很简单,签个名。

假设我的服务器有个秘密字符串,是 this_is_my_secret_and_fuck_you_all,我为用户 cookie 的 dotcom_user 字段设置了个值 alsotang。cookie 本应是{dotcom_user: 'alsotang'}这样的。

而如果我们签个名,比如把 dotcom_user 的值跟我的 secret_string 做个sha1sha1('this_is_my_secret_and_fuck_you_all' + 'alsotang') === '4850a42e3bc0d39c978770392cbd8dc2923e3d1d'

然后把 cookie 变成这样

{   dotcom_user: 'alsotang',  'dotcom_user.sig': '4850a42e3bc0d39c978770392cbd8dc2923e3d1d', }

这样一来,用户就没法伪造信息了。一旦它更改了 cookie 中的信息,则服务器会发现 hash 校验的不一致。

毕竟他不懂我们的 secret_string 是什么,而暴力破解哈希值的成本太高。 

 

5. 用户收到HTTP响应后,便看不到任何敏感数据了。在此后的请求中发送该Cookie给服务器。

6. 服务器收到此后的HTTP请求后,发现Cookie中有SessionID,进行防篡改验证。

7. 如果通过了验证,根据该ID从Redis中取出对应的用户对象, 查看该对象的状态并继续执行业务逻辑。

下面是写的demo代码,可以跑起来,但是记得安装redis

<!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>
    <script src="./lib/jquery-3.3.1.js"></script>
</head>

<body>
    <!-- <form> -->
    <label for="user">用户名:</label>
    <input type="text" id="user" value="">
    <label for="pwd">密码:</label>
    <input type="password" id="pwd" value="">
    <button id="confirm">确定</button>
    <!-- </form> -->
    <script>
        var user = document.getElementById('user')
        var pwd = document.getElementById('pwd')
        user.addEventListener('change', function (e) {
            user.value = e.target.value
        })
        pwd.addEventListener('change', function (e) {
            pwd.value = e.target.value
        })
        document.getElementById('confirm').addEventListener('click', function (event) {
            $.ajax({
                type: "POST",
                url: "/login",
                data: {
                    name: user.value,
                    password: pwd.value
                },
                success: function (data) {
                    console.log(data)
                    if (data.ret_code === 0) {
                        window.location.href = '/home'
                    }
                }
            });
        })
    </script>
</body>

</html>

 node index.js

const express = require('express');
const session = require('express-session');
const redis = require('redis');
const RedisStore = require('connect-redis')(session);
const cookieParser = require('cookie-parser');
const static = require('express-static')
const bodyParser = require('body-parser');
const path = require('path');
const app = express();

const secret = "wang111";
const sessionName = 'session_id';
var users = require('./user').items;
var findUser = function (name, password) {
  return users.find(function (item) {
    return item.name === name && item.password === password;
  });
};

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use('/lib', static(__dirname + '/lib'));
// 设置 Cookie
app.use(cookieParser());
// 设置 Session
app.use(session({
  secret: secret,
  //重新保存:强制会话保存即使是未修改的。默认为true但是得写上
  resave: true,
  //强制“未初始化”的会话保存到存储
  saveUninitialized: false,
  name: sessionName,
  cookie: { maxAge: 60 * 60 * 24 * 1 },
  store: new RedisStore({ // redis-server etc/redis.conf  redis-cli
    host: '127.0.0.1',
    port: '6379',
    db: 0,
    pass: '',
    ttl: 60 * 60 * 24 * 1, //session的有效期为1天(秒)
  })
}));

app.get('/', function (req, res) {
  if (req.session.login) {
    res.redirect('/home');
  }
  res.sendFile('index.html', {
    root: path.join(__dirname, ''),
  });
});

app.post("/login", function (req, res) {
  var userExist = findUser(req.body.name, req.body.password);
  if (userExist) {
    req.session.user = req.body.name;
    req.session.login = true;
    res.json({ ret_code: 0, ret_msg: '登录成功' });
  } else {
    res.json({ ret_code: 1, ret_msg: '账号或密码错误' });
  }
});

app.get('/home', function (req, res) {
  res.sendFile('home.html', {
    root: path.join(__dirname, ''),
  });
});

app.get('/logout', function(req, res){
  req.session.destroy();
  res.redirect('/');
})

app.listen(9080);

node user.js

module.exports = {
    items: [
        {name: 'wang', password: '123'}
    ]
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值