cookie 和 session的应用场景

3 篇文章 0 订阅
2 篇文章 0 订阅

不管怎么样,还是要先扯一下,cookie为什么有这么个玩意儿。那就得说下http协议,因为http协议是一个无状态的协议,什么是无状态,就是在客户端向服务器请求页面的时候,无论是静态还是动态,是首页,还是说别的页面,就是说不管什么页面;比如:我请求了一个链接:www.baidu.com,我在浏览器窗口输入这个地址之后,按回车,那么就是我客户端通过浏览器向服务器发送了一个请求,那么一般情况下,打个比方,我服务端不知道客户端是张三还是李四发的请求,因为我服务端没法识别你客户端;有人会说通过ip,不是在请求的时候,在服务器端可以获取客户端请求的ip地址吗,如果一台固定的电脑,每次都请求百度这个网站,我不就知道你客户端上次什么时候向我服务端发送过请求吗,但是我想说,如果你的电脑在一个大的公司当中,有很多台电脑,那你们这个公司的网络在同一个路由上,那么这些电脑就用的是同一个公网ip地址;那服务器端能区分是哪台电脑发起的请求吗?更何况,在一台电脑上,分别有n个不同的人发起了同一个地址请求,那就分不清楚到底是谁发起的了,话说回来,我们这里先不讨论一个机器n个人使用的情况,因为我们需要慢慢的深入,一步步把这个逻辑铺展开来,那又有人说了,我不区分就行了吗,管你是谁发的请求,但是现实告诉我们,服务器端有时候就是想根据客户的信息来响应不同的数据给不同的客户呢?那么就是说服务端识别客户端的身份是势在必行,大势所趋的,所以我们发明了一个叫cookie的玩意儿。

那么究于以上无法识别的原因,cookie诞生了,请记住,这里session还没有诞生,先不管他。那么cookie的原理就是:

比如:我第一次通过浏览器向服务端发送请求 www.baidu.com,在第一次发送的时候,为了让服务器记住我是谁,我可以设置一个字符串,或者对象什么的,随着我对网页的请求发送给服务端,服务端拿到这个数据之后,一看这个字符串和对象,哦!原来是你啊,我知道你,他就把你记住了.......

那这个字符串和对象怎么携带呢?那比如我是百度网站的开发人员,如果我想让客户端请求www.baidu.com这个网站的时候,也就是闹着玩,没啥目的,就是想验证下,我能不能收到客户端发来的这个字符串或对象;那我怎么做?事实上第一次请求客户端啥都携带不了给服务端;只可以服务端去做这件事情,那么我可以用cookie这个对象的方法在www.baidu.com这个网站里面(在编写www.baidu.com这个网页的时候)的script部分写一个:

document.cookie='name=我是客户端的一个信息';

设置好了之后,哪怕是在本地,当然网站最终是要放回的服务器的,我的意思就是你在本地和服务器都可以验证现在正在验证的情况;我们在浏览器请求网站,打开console后台分别可以在application和network里面看到你设置的cookie

 

也就是说我在请求baidu.com的时候,我获得了一个cookie,name是name,value是“我是客户端的一个信息”,浏览器把它保存在了我们的电脑的某个位置;浏览器每次请求baidu.com都会在requestHreader里面把这个信息带上去向服务器去请求baidu.com,这样服务器每次都可以分辨,哦!原来是“我是客户端的一个信息”发来的请求。。。然后服务端也许会想,“这小子不错,除了给他返回百度主页意外,再给他回复个信息叫:‘hello 好久不见’”,那么客户端除了收到自己请求的百度主业外还会收到额外信息,至于这个额外信息以什么方式让客户知道,那就都小意思了,说这个额外的信息,其实就是设立cookie身份识别的本质,就是服务端要识别不同的客户,来做出不同的响应,这就是cookie的目的;

好了,继续说。可能有人会问,那我每次请求百度首页会有一个cookie携带给服务端(实际上第一次请求也是服务端给客户端的),那我昨天请求过,并且请求完之后,把电脑关了,那我今天还想请求,服务端还认识我吗?这个问题就牵扯到2个问题了,一个问题是cookie的时效性,我把电脑关了,它还存在吗?第二个问题是我们设置的name是写死的,就是说无论谁请求百度首页,都会生成相同的name的值的cookie,第二个问题很好解决,怎么解决呢,就是我们假设你要请求我百度首页,我服务端必须识别你的身份,如果你不是我的会员那我就在服务端重定向,给你响应的页面是一个注册页面,你客户端注册完之后,假设就直接跳转的百度主页去,显示你是登录状态;其间,你在注册完毕的时候,我就给你设置一个cookie,比如:

document.cookie="username=your.password"

那么我注册完毕再次跳转到首页的时候就携带了一个cookie,有人又会问了,那这个cookie是哪个页面给客户发的呢?其实啊我们不用去在意是客户在完成或者请求哪个页面的时候服务端给客户发的,因为cookie它有一个path属性,可以设置路径,就是我这个cookie(客户端已经保存的cookie),在请求哪些页面的时候,我的客户端的cookie会被携带在请求头里面,哪些不会被携带,我们可以通过path来设置,比如客户端是在注册页面,填写完注册信息,点击提交按钮后,服务端通过返回一个

document.cookie="username=your.password;path='/'";

给客户端了一个cookie,然后服务端重定向让客户端页面跳转到了百度首页, 如果path设置的是根目录那就是说无论客户端请求服务端的什么页面,浏览器在每次请求的时候都会携带客户端的cookie给服务器,服务器在有必要的时候,在服务端验证你的cookie是不是它要求的,比如会员的账户,密码信息,当然这些cookie在服务端给用户端的时候都会加密,当服务端再次收到的时候都会解密识别;

另外一个问题就是关闭电脑后再次请求,这个问题可以设置cookie的另外一个属性去完成,expires,这里就不说它了;

好了说到这里,基本上把cookie已经把最原始的功能说完了,那么非原始的功能是什么?其实就是存数据,比如把客户的信息存到cookie里面,客户端再去访问特定页面的时候,服务端再去获取它,来执行不同的逻辑;

以上是cookie的原理,但是cookie有一个致命的缺点,那就是在客户端,客户可以自己去设置cookie的参数,比如,客户在控制台敲入下面代码:

document.cookie="usernmae='呃呃呃'"

那cookie的值就被改变了,那么服务端获取的数据也就变了,可见,cookie的最大缺点是安全性;

那么这个时候session就应运而生了,实际上session的实现原理还是基于cookie的,有些人就想了,说有了session,就没必要学习cookie了,其实答案是不可以,因为session是基于cookie,没有cookie就没有session,下面我们来说说session 

事实上,当客户端第一次请求页面的时候,所携带的cookie是一个空值,就是啥都没有,那这个时候我们可以通过session设置一个session_id:xxxxxx,这个session_id:xxxxx被设置在了cookie里面有一个session_id:xxxxxx属性;那么等到第二次请求该页面的时候,客户端携带的cookie里面就有了这个session_id:xxxxxx,那服务端就可以通过读取sesssion_id的值,来获取服务端session保存的值;

隐患:但是有一个问题,就是这个session_id在客户端浏览器控制台是可以看到的,比如,我登录了会员,在我电脑上就生成了一个cookie,当我上厕所的时候,有人在我电脑把我这个cookie拷贝了一份拿走了。。。那们这个人就可以通过这个cookie来登录我的会员,这被称为劫持;

关于劫持那点事儿我们在这不说哈;go on

那么在客户端发起请求页面的时候,有时候,服务端就会根据请求的某个页面发送一个session_id给客户端,保存在cookie中,

那么当客户以后再请求的时候,就会在请求头当中携带这个session_id,实际上这个session_id对应的那个字符串到底是什么玩意儿,呵呵(说实话,我至今似懂非懂),安我们大多数的人的理解,它 就是一个加过密的字符串,至于这个字符串没加密之前是啥模样,我还真不懂(应该就是在服务端设置的req.session.xxx=xxx,这个是express-session里面的设置方法),就是后者的xxx进行加过密,特殊处理的一个字符串,发到客户端了,(或者就是服务器根据req.session.xxx=xxx对应设置了一个session_id),总之,就是和你服务器req.session.xxx=xxx这个保存的数据没多大关系,唯一的关系就是你第二次请求页面的时候,把这个session_id携带上,服务器一识别,就能获取session_id在服务器上所对应的你保存的一些信息,服务器端再通过逻辑处理用这个信息和数据库的数据进行对比,确认没问题后,服务器就会返回给客户端一些特定的响应(比如识别客户端是会员、、、什么的)就会有不同的权限了。。。

虽然服务端通过req.session.xxx=xxx设置的session_id,相对同一个客户端请求来说,应该是相同的,也就是说,不管你猴年马月向服务端请求,服务端发给你的session_id应该是相同的,其实啊,每次都是不同的,这是服务端做的算法和加密决定的,基本不可能一样;但是如果销毁session(关闭会话的意思,什么是关闭会话,可以理解为,不保持已识别状态)之后,咳咳,为啥要销毁,比如会员登出操纵就需要销毁session,go on,我们在application里面仍然可以看到session_id这个cookie值;刚学习的时候我页很烦恼,为啥都销毁了,怎么在console里面还能看到呢???

实际上我们要理解,session的底层实现原理是cookie这句话,上面说的销毁session只是关闭了会话,可并没有把客户端的cookie里的session_id给删除啊,事实上,当你销毁session的时候,就好像服务端给客户说"OK,那个session_id作废了“,要想再次获取页面,你需要再次登录页面,我会给你一个新的session_id用来识别你;

也就是说之前的那个session_id作废了,虽然还可以在console看到,但是它已经没啥用了,有的人看的心慌。。。那你也可以把它给删了,在express-session里面用

 res.clearCookie('session_id');

就可以把application里面的session_id删掉了。

除了上面讲的一些大的原理之外,再就是一些path,expries,domain,等的一些设置了,这些就不用赘述了,非常简单。

下面把我的代码粘贴出来,供大家参考:

app.js代码:

//导包
var express = require('express');
var router = require('./routes/router.js');
var bodyParser = require('body-parser');
var session = require('express-session');

//创建服务器
var app = express();

// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));
// parse application/json
app.use(bodyParser.json());
//模板引擎,html结尾文件渲染
app.engine('html',require('express-art-template'));
app.set('view options', {
    debug: process.env.NODE_ENV !== 'production'
});
//设定渲染的路径,第一个参数是views,第二个参数是要改变的路径,默认views
// app.set('views','./public/');
//开放node_modules文件夹
app.use('/node_modules',express.static('./node_modules/'));
app.use('/public',express.static('./public/'));
//挂载session中间件
app.use(session({
    //secret,配置加密字符串,它会在原有的基础上再和secret的值去拼接加密
    //目的是加强安全性,防止客户端恶意伪造
    name:'session_id',
    secret: 'thisisastring',
    resave: false,
    saveUninitialized: false//true 无论是否使用session,默认只要对页面发起请求,都会给客户端一个cookie
}));
//路由
app.use(router);


//监听
app.listen(3000,function () {
    console.log('server is running');
});

router.js代码:

var express = require('express');
var User = require('../model/user');
var md5 = require('blueimp-md5');
var router = express.Router();

router.get('/',function (req,res) {
    //如果注册登陆成功后就跳转,并用session中的user数据去渲染用户信息;
    res.render('index.html',{user:req.session.user});
});

router.get('/login',function (req,res) {
    res.render('login.html');
});
router.post('/login',function (req,res) {
    req.body.password = md5(req.body.password);
    User.findOne(req.body).then(function (data) {
        if (data){
            //如果没有session就给设定一个session
            if (!req.session.user) {
                req.session.user = data;
            }
            res.status(200).json({
                err_code : 1,
                message : '数据库验证成功'
            });
       }else{
            res.status(200).json({
                err_code :0,
                message : '用户名或密码错误'
            })
       }
    },function (error) {
        res.status(500).json({
            err_code : 500,
            message :'服务端错误'
        })
    });
});

router.get('/register',function (req,res) {
    res.render('register.html');
});
//用户名是否已经注册
router.post('/register/username',function (req,res) {
    //两种情况,一种是用户名的的校验,另外一种是提交的注册信息
    //如果是校验,那么返回的值是{'valid': true or false},true表示可用
    User.findOne({username:req.body.username})
        .then(function (data) {
            if (data){
                res.json({'valid':false});
            }else{
                res.json({'valid':true});
            }
        });
});
//注册
router.post('/register',function (req,res) {
    req.body.password = md5(req.body.password);
    new User(req.body).save()
        .then(function (data) {
            //给客户端一个session,session里面的user属性保存了客户的信息;
            req.session.user = data;
            res.status(200).json({
                //这里用err_code的值去代表状态比较好
                err_code:1,
                message:'ok'
            });
    },function (error) {
            res.status(500).json({
                err_code : 500,
                message :'服务端错误'
            })
        });
});
router.get('/logout',function (req,res) {
    //如果点击后就销毁session
    // req.session.destroy(function(err) {
    //     // cannot access session here
    // });
    req.session.user =null;
    res.clearCookie('session_id');
    res.status(302).redirect('/login');
    console.log(21221);
});
module.exports = router;

写了一天好累。。。

 

  • 10
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值