Session驗證用戶登陸的大體過程

1. 用戶提交包含用戶名和密碼的表單,發送HTTP請求。javascript

2. 服務器驗證用戶發來的用戶名密碼。html

3. 若是正確則把當前用戶名(一般是用戶對象)存儲到redis中,並生成它在redis中的ID。 這個ID稱爲Session ID,經過Session ID能夠從Redis中取出對應的用戶對象, 敏感數據(好比authed=true)都存儲在這個用戶對象中。java

4. 設置Cookie爲sessionId=xxxxxx|checksum併發送HTTP響應, 仍然爲每一項Cookie都設置簽名。node

 

signedCookiejquery

計算機領域有個名詞叫 簽名,專業點說,叫 信息摘要算法。web

好比,咱們如今面臨着一個菜鳥開發的網站,他用 cookie 來記錄登錄的用戶憑證。相應的 cookie 長這樣:dotcom_user=alsotang,它說明如今的用戶是 alsotang 這個用戶。若是我在瀏覽器中裝個插件,把它改爲dotcom_user=ricardo,服務器一讀取,就會誤認爲我是 ricardo。而後我就能夠進行 ricardo 才能進行的操做了。ajax

以前 web 開發不成熟的時候,用這招甚至能夠黑個網站下來,把 cookie 改爲 dotcom_user=admin 就好了。redis

如今是怎麼保證不被篡改呢?答案很簡單,籤個名。算法

假設個人服務器有個祕密字符串,是 this_is_my_secret_and_fuck_you_all,我爲用戶 cookie 的 dotcom_user 字段設置了個值 alsotang。cookie 本應是{dotcom_user: 'alsotang'}這樣的。express

而若是咱們籤個名,好比把 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'}
    ]
};
相關文章
相關標籤/搜索