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'} ] };