Cookie与Session

2020-05-28  本文已影响0人  憨憨二师兄

Cookie简介

为什么有cookie

以下内容摘自维基百科:

为什么有Cookie?
因为HTTP协议是无状态的,即服务器不知道用户上一次做了什么,这严重阻碍了交互式Web应用程序的实现。
在典型的网上购物场景中,用户浏览了几个页面,买了一盒饼干和两瓶饮料。
最后结帐时,由于HTTP的无状态性,不通过额外的手段,服务器并不知道用户到底买了什么。
所以Cookie就是用来绕开HTTP的无状态性的“额外手段”之一。
服务器可以设置或读取Cookies中包含信息,借此维护用户跟服务器会话中的状态。

在刚才的购物场景中,当用户选购了第一项商品,服务器在向用户发送网页的同时,还发送了一段Cookie,记录着那项商品的信息。
当用户访问另一个页面,浏览器会把Cookie发送给服务器,于是服务器知道他之前选购了什么。
用户继续选购饮料,服务器就在原来那段Cookie里追加新的商品信息。
结帐时,服务器读取发送来的Cookie就行了。

Cookie另一个典型的应用是当登录一个网站时,网站往往会请求用户输入用户名和密码,并且用户可以勾选“下次自动登录”。
如果勾选了,那么下次访问同一网站时,用户会发现没输入用户名和密码就已经登录了。
这正是因为前一次登录时,服务器发送了包含登录凭据(用户名加密码的某种加密形式)的Cookie到用户的硬盘上。
第二次登录时,如果该Cookie尚未到期,浏览器会发送该Cookie,服务器验证凭据,于是不必输入用户名和密码就让用户登录了。

以上内容是维基百科对Cookie的解释,为什么有Cookie? 说白了: 因为浏览器与人需要一种交互,Cookie则实现了这种交互;服务器通过向用户发送Cookie给予了用户某种“权限”,这好比是一张门票,当浏览器携带着“门票”向服务器发起请求时,服务器通过验证“门票”信息就可以赋予用户某种“权限”。接下来我用注册与登陆的示例来简单地介绍下 Cookie的具体应用场景。

注册登陆

本案例中,注册登陆做的并不全面,但是基本思想和真实的注册登陆还算吻合。案例中并没有真实的数据库,而是使用了json文件充当数据库,json文件中仅有一个数组,用来存储用户的信息。当用户注册成功后,则将用户的账号密码存储到我们的"数据库"中,在此之前需要通过前端校验代码以及后端的校验代码,本案例中仅有前端的校验,而后端校验代码并没有在案例中展示。在校验成功时,页面会由注册界面自动跳转到登陆界面,并且用户的信息会被添加到json文件中,如果用户再次使用相同的邮箱进行注册时,界面则会提示“用户已存在”的信息。示例效果如下:



如果注册成功,json文件中则会新增用户的信息:



再次使用该邮箱进行注册时:



跳转到登陆界面后,如果用户输入的邮箱及密码与数据库的数据信息匹配,则登陆成功,页面会跳转至index页面,同时服务器会在返回给客户端的响应头中添加cookie,在cookie中存入了用户的信息:

if(isEmailMatch && isPasswordMatch){
    response.setHeader('Set-Cookie',[`mysite_email=${userMessage.email};Max-Age=300;HttpOnly`,`mysite_password=${userMessage.password};Max-Age=300;HttpOnly`]);
    response.statusCode = 200;
}else if(isEmailMatch && !isPasswordMatch){

    //  错误代码 401 : 未授权,登陆失败
    response.statusCode = 401;
    response.setHeader('Content-Type','application/json;charset=utf-8');
    response.write(`
        {
            "errors":{
                "error":"passwordWrong"
            }
        }
    `);
}else if (!isEmailMatch){
    response.statusCode = 401;
    response.setHeader('Content-Type','application/json;charset=utf-8');
    response.write(`
        {
            "errors":{
                "error":"notRegister"
            }
        }
    `);
}

本段代码中如果用户登陆的邮箱及密码均正确则服务器会在响应头中“Set-Cookie”,本示例中演示了如何设置多个cookie的方法即即:

response.setHeader('Set-Cookie',['cookieName1=val1','cookieName2=val2']);

同时在Set-Cookie中 Max-Age代表在 cookie 失效之前需要经过的秒数;设置了 HttpOnly 属性的 cookie 不能使用 JavaScript 经由 Document.cookie 属性、XMLHttpRequest 和 Request APIs 进行访问,以防范跨站脚本攻击(XSS)。如果需要了解具体详细的Set-Cookie设置信息可以参考 MDN的Set-Cookie
在代码中,我也做出了一些最简单的后台判断,例如邮箱输入正确但密码输入错误时,服务器reponse一个json格式的数据流,前端代码通过判断会将错误信息反馈至页面上,部分代码如下:

// ajax post
$.post('/login',userMessage)
    .then(
        // success
        ()=>{
        // 跳转至 index 页面
        window.location.href = '/'
        },
        // fail
        (request)=>{
            let errorMessage = request.responseJSON.errors.error;
                        
            if(errorMessage === 'passwordWrong'){
                errorText("password","密码错误");
                return
            }else if(errorMessage === 'notRegister'){
                errorText("email","无此用户信息");
                return
            }
        }
    )

具体的效果如下:


密码错误:



无此用户信息:



在登陆成功后,跳转至index主页,当浏览器向服务器发起get请求获取主页时,因为浏览器中携带了同域名的cookie信息,也就是携带了"入场票据",在服务器检查了"票据"后,响应给用户一个经处理过的页面:



如果我们在未携带cookie信息访问index页面时,则会获取到这样的内容~



在点击退出登陆后,页面会跳转到login页面,同时也会清除cookie的信息,当然我们需要先将后台代码的 Set-Cookie中的HttpOnly删除掉!! 如果不将HttpOnly删除,那么我们是无法调用document.cookie的~ 退出登陆button事件的代码如下:

// 退出登陆时清空 cookie
$('#clearCookie').on('click',()=>{
    deleteCookie('mysite_password');
    deleteCookie('mysite_email');
    window.location.href = '/login';
})
function deleteCookie (name){
    // 设置让 cookie 无效的方法 让cookie的期限 expires 为 现在
    document.cookie = name + '=; expires='+new Date().toGMTString();
}

我们看到了Cookie在注册登陆中的作用,当用户携带着某域名可以识别的Cookie时,服务器就可以通过用户的"票据"返回给用户不同的页面,如果我们未注册登陆直接访问 index主页,获得到的信息与携带Cookie访问主页时获得的信息是截然不同的。这就是Cookie应用最多的一个场景,但是这样做实际上也是有问题的,用户完全可以仿造Cookie,只要用户有一点点的HTTP知识,修改Cookie伪造用户是完全可以的,这就难免带来了安全问题。

Session

Cookie带来的问题就是安全问题,它可以被任意地修改和伪造,于是Session就出现了,在说明什么是Session之前,我们先试图去改进代码带来的安全性问题:


首先在 server.js 中 声明一个全局变量 mySession

var mySession = {};

在用户登陆成功之后,传给用户的cookie就不是用户的信息了,我们传给用户一个sessionId,sessionId的值是一个随机数:

if(isEmailMatch && isPasswordMatch){
    // add Session
    let sessionId = Math.random();
    mySession[`${sessionId}`] = {'mysite_email':userMessage.email,'mysite_password':userMessage.password};

    response.setHeader('Set-Cookie',`sessionId=${sessionId}`);
    response.statusCode = 200;
}

当用户访问index首页时,我们需要对用户cookie中 sessionId的值进行判断:

if(path === '/'){
    let string = fs.readFileSync('./index.html','utf8');
    //  如果请求头中 携带 cookie 信息
    if(request.headers.cookie){
    let hash = {};
    request.headers.cookie.split('; ') .forEach((item)=>{ 
        if(item.split('=')[0] === 'sessionId' && mySession[item.split('=')[1]]){
            hash['mysite_email'] = mySession[item.split('=')[1]].mysite_email;
            hash['mysite_password'] = mySession[item.split('=')[1]].mysite_password;
        }
    });
    ... ...
}

我们仅仅修改了几行代码,cookie中不再存入用户的隐私信息而是改为了sessionId 而sessionId对应的value是一串随机数,后台代码只需要验证这串随机数即可,这样依赖即便用户拥有修改cookie的能力,也无能为力,因为cookie存储在客户端,而session存储的隐私信息都是在服务器上的。cookie携带的"票证"仅仅是sessionId,服务器则是通过cookie携带的sessionId 来进行判断的。我们来总结下:

Cookie:

  1. 服务器通过Response-Header 的 Set-Cookie给客户端一串字符串
  2. 客户端每次访问相同域名的网页时,带上这串字符串,服务器可以通过这串字符串去读取客户端的信息
  3. 客户端要在一段时间内保存这个Cookie
  4. Cookie默认在用户关闭页面后就会失效,但是后台代码可以任意设置Cookie的过期时间

Session:

  1. 将SessionId通过Cookie发给客户端
  2. 客户端访问服务器时,服务器读取SessionId
  3. 服务器有一块内存保存了所有的Session
  4. 通过SessionId可以得到对应用户的隐私信息
  5. 这块内存就是服务器上的所有session

Session与Cookie的联系与区别:

  1. Session是依赖于Cookie实现的
  2. Cookie存储在客户端,Session则存储在服务器上
上一篇 下一篇

猜你喜欢

热点阅读