1. 程式人生 > >全面了解cookie和session

全面了解cookie和session

之前 find 方式 .html har 現在 target 服務器 blog

  • http協議:

http即超文本傳輸協議(萬維網定義的),一種基於瀏覽器請求與服務器響應的鏈接,它是一個很純粹的傳輸協議。http協議主要的特征就是它是一種無狀態的協議(只針對cookie與session問題),在客戶端連續向服務器發送請求的時候,每次請求的過程中只要數據交換完畢,服務器與客戶端就會斷開連接,再次請求的時候會重新連接客戶端與服務器,這樣服務器記錄上次的對話,那麽問題來了,如何讓服務器知道是哪個客戶端向自己發出的請求呢,這個時候cookie就誕生了~

  • 什麽是cookie

cookie是一小段文本信息,這段小文本信息由服務器首次響應客戶端時發送的,在客戶端向服務器首次發送請求的時候,服務器會判斷是否要記錄客戶端的身份,如果需要,此時就會在響應中(response)給客戶端發送一個cookie,該cookie文本信息保存在http的報頭裏,當瀏覽器會將cookie保存起來,當該瀏覽器再次發送請求時會攜帶cookie,服務器檢查cookie來識別瀏覽器請求,這裏cookie的特征就不在說明了。下面我們上代碼!

頁面代碼:

技術分享圖片
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <script type="text/javascript" src="jquery-3.3.1.min.js"></script>
    <script type="text/javascript" src=‘test.js‘></script>
</head>
<body>
    <section>
        <h3>Register</h3>
        <div>
            <label style="display:inline-block; width: 100px;" id="register-user-name-label" htmlfor="register-user-name-input">register:</label>
            <input style="display:inline-block; width: 200px;" id="register-user-name-input" type="text" />
        </div>
        <div>
            <label style="display:inline-block; width: 100px;" id="register-password-label" htmlfor="register-password-input">pasword:</label>
            <input style="display:inline-block; width: 200px;" id="register-password-input" type="text" />
        </div>
        <button id="register" type="button">Register</button>
    </section>
    <section>
        <h3>Login</h3>
        <div>
            <label style="display:inline-block; width: 100px;" id="user-name-label" htmlfor="user-name-input">login name:</label>
            <input style="display:inline-block; width: 200px;" id="user-name-input" type="text" />
        </div>
        <div>
            <label style="display:inline-block; width: 100px;" id="password-label" htmlfor="password-input">pasword:</label>
            <input style="display:inline-block; width: 200px;" id="password-input" type="text" />
        </div>
        <button id="login" type="button">Login</button>
    </section>
    <script type="text/javascript" src=‘test.js‘></script>
</body>
</html>
技術分享圖片

很簡單,一個註冊按鈕一個登陸按鈕(ps:代碼冗余請忽視,就是為了做個demo用)。

首先註冊一個user:

註冊user之後我們查看db

技術分享圖片

然後我們用這個user進行登陸操作,重點來啦~

首先刷新下頁面,調用獲取user方法,看下效果

代碼如下:

技術分享圖片
app.get(‘/userInfo‘, function (req, res) {
    //cookie
    if (req.cookies.userInfo) {
        console.log(‘login successfully‘)
    }
    else {
        console.log(‘session timeout.‘);
    }
    res.status(200).json(req.cookies.userInfo);
    //session
    // if (req.session.userInfo) console.log(‘login successfully‘);
    // else console.log(‘session timeout.‘);
})
技術分享圖片

技術分享圖片

好了先mark下,回頭再來做對比,下面執行login操作,這裏要上代碼了。

首先引入一個中間件:

var cookie = require(‘cookie-parser‘);

使用它:

技術分享圖片
app.use(cookie(‘express_cookie‘));
//cookie
app.post(‘/login‘, function (req, res) {
    User.findOne({
        username: req.body.username
    }).then(function (userInfo) {
        if (!userInfo) {
            console.log(‘user is not exist.‘);
            return;
        }
        var data = {};
        data[‘username‘] = userInfo.username;
        data[‘password‘] = userInfo.password;
        res.cookie(‘username‘, JSON.stringify(data), { maxAge: 900000, httpOnly: true });
        res.status(200).json(data);
    })
        .catch(function (e) {
            console.log(e);
        })
})
技術分享圖片

這裏我們可以設置cookie的httpOnly屬性,最大生命周期,等等,然後我們先在db內查詢當前登錄user,如果已經註冊過,我們獲取user信息並存入cookie中。這時候看下前端的響應有什麽不同。

技術分享圖片

可以看見,服務器頒發的cookie在響應的header中的Set-Cookie中。似不似發現不同了。這時候我們在刷新下頁面調用userInfo方法看下效果。

技術分享圖片

咦,我們發現這次的請求裏面居然有cookie了,就這麽神奇(Ps:我們要相信科學!)。

debug下看看

技術分享圖片

服務器端有我們想要的cookie信息了。這樣服務器就可以根據cookie知道了我們每一次的請求是不是同一個人了。

總結:首先cookie是服務器頒發的,然後隨著響應返回給客戶端也就是我們的瀏覽器,瀏覽器保存cookie,每一次發送請求都會帶著這個cookie來讓服務器知道,嗯我就是上次的那個人,到這裏對cookie是不是多少了解了一些呢~

好了,那麽現在很多瀏覽器都是禁用cookie的,原因是啥呢~,由於cookie是可以被獲取的以及cookie是可以修改的,這時候引出了web安全方面的姿勢,跨站腳本攻擊以及跨站協議偽造,可以參考我之前寫的關於XSS攻擊(戳我:什麽是XSS以及CFRS),那麽如果cookie禁用了我們該怎麽辦呢?這時候session就誕生了。

  • 何為session:

session本省並不存在,只是一個概念,session是服務器用來記錄客戶端狀態的機制,不同於cookie保存在瀏覽器中,session是保存在服務器上的,服務器會根據cookie生成一個session id存在服務器上,當請求再次抵達服務器時,服務器發出響應時會將session id 存在cookie內一同反回給瀏覽器,這就是session。session具體哪些特點這裏就不寫啦,話不多說,上代碼。

首先引入一個中間件:

var session = require(‘express-session‘);

使用它

技術分享圖片
app.use(cookie(‘express_cookie‘));
app.use(session({
    secret: ‘express_cookie‘,
    resave: false,
    saveUninitialized: true,
    cookie: { maxAge: 60 * 1000 * 30 },
    rolling: true,
}));

app.post(‘/login‘, function (req, res) {
    User.findOne({
        username: req.body.username
    }).then(function (userInfo) {
        if (!userInfo) {
            console.log(‘user is not exist.‘);
            return;
        }
        var data = {};
        data[‘username‘] = userInfo.username;
        data[‘password‘] = userInfo.password;
        req.session.userInfo = data;
        res.status(200).json(data);
    })
        .catch(function (e) {
            console.log(e);
        })
})
技術分享圖片

現在我們登錄一下看下效果:

技術分享圖片

會發現,多了一個Cookie,而且Cookie裏面多了一個sid,不用聯想了,這就是sessionId,這時候我們在刷新一下頁面看下userInfo變成啥樣了呢?

技術分享圖片

可以清晰的看到再次請求的時候,sessionId會裝在Cookie中,然後發送給服務器,這時候服務器就知道了,咦,原來是上個人。這就是session。

由於現在服務器session存入的方式我們采用了服務器自帶的內存,也叫session memory。如果server掛了怎麽辦呢,掛掉了內存就釋放了啊,session就沒了啊。這個時候就引出了另外一個問題,session的可持續化。

  • session的可持續化

session的可持續化方式簡單的理解就是讓session可以在生命周期內一直存在,可以把session存入db中,可以是MongoDB,可以是redis,上代碼,我們這裏用MongoDB吧,個人比較喜愛。

引入中間件:

var MongoStore = require(‘connect-mongo‘)(session);
技術分享圖片
app.use(cookie(‘express_cookie‘));
app.use(session({
    secret: ‘express_cookie‘,
    resave: false,
    saveUninitialized: true,
    cookie: { maxAge: 60 * 1000 * 30 },
    rolling: true,
    store: new MongoStore({
        url: ‘mongodb://127.0.0.1:27017/demo‘,
        collection: ‘sessions‘
    })
}));
app.post(‘/login‘, function (req, res) {
    User.findOne({
        username: req.body.username
    }).then(function (userInfo) {
        if (!userInfo) {
            console.log(‘user is not exist.‘);
            return;
        }
        var data = {};
        data[‘username‘] = userInfo.username;
        data[‘password‘] = userInfo.password;
        req.session.userInfo = data;
        res.status(200).json(data);
    })
        .catch(function (e) {
            console.log(e);
        })
})
技術分享圖片

http請求與響應部分我們就不看了,直接看server跟DB。

server中我們將userInfo放入session中

var data = {};
data[‘username‘] = userInfo.username;
data[‘password‘] = userInfo.password;
req.session.userInfo = data;

查看DB

技術分享圖片

咦,一條session就在DB中誕生了,這裏要註意的是,session不是設置的時候就會存入DB中的,包括內存等等,而且響應成功的時候才會存入,一定要註意,不然坑的就是你。

然後刷新頁面看下效果。

技術分享圖片

似不似,session中就有了user信息。好了到這裏關於session持久化的問題也解決了。

登出功能就很簡單了,銷毀session就ok了,代碼如下:

app.get("/loginOut",function(req,res){
    req.session.destroy(function(err){
        console.log(err);
    })
    res.send(‘退出登錄成功‘);
});

Redis方式:

中間件以及使用:

技術分享圖片
var RedisStrore = require(‘connect-redis‘)(session);
app.use(session({
    secret: ‘express_cookie‘,
    resave: false,
    saveUninitialized: true,
    cookie: { maxAge: 60 * 1000 * 30 },
    rolling: true,
    store: new RedisStrore({})
}));
技術分享圖片

總結:第一次登陸請求的時候,服務器會頒發一個sessionId,響應的時候將sessionId放入cookie中返回給瀏覽器,此時session已存入DB中,當再次請求的時候攜帶著sessionId進入服務器中,獲取session信息,服務器還是會記得我

全面了解cookie和session