Session、LocalStorage、Cache Control

项目地址css

使用session保存信息

Cookie 存在的问题

用户能够随意篡改 Cookiehtml

Session 与 Cookie 的关系

通常来讲,Session 基于 Cookie 来实现。前端

Cookie

1.服务器经过 Set-Cookie 头给客户端一串字符串vue

2.客户端每次访问相同域名的网页时,必须带上这段字符串html5

3.客户端要在一段时间内保存这个Cookie(cookie是保存在客户端的)node

4.Cookie 默认在用户关闭页面后就失效,后台代码能够任意设置 Cookie 的过时时间 大小大概在 4kb 之内react

好比:git

i.当用户登陆成功,用户就会获得一个Cookie好比是sign_email:1@Reagen.com程序员

ii.当用户访问首页的时候,请求中就包含了cookie中的字符串github

iii. 服务器读Cookie,发现用户是以sign_email:1@Reagen.com这个信息登陆的,就从数据库把用户相关的信息渲染到html中而后再返还给用户,若是服务器发现读的cookie是空的,那么服务器就会返回和以前登陆页面不一样的一个页面

以前说过Cookie是能够被用户篡改的,也就是若是用户篡改了本身的Cookie它就能够以另外一种身份去登陆或者向服务器发请求了

好比:将页面中的1030052539@qq.com改成1@Reagen.com后刷新页面咱们就可获得保存再数据库中的用户1@Reagen.com对应的密码了,这就是cookie的弊端,用户能够修改cookie的id,而后把id对应的密码明文的展现出来,很危险

怎么办?session能够解决这个问题

session

看下以前的注册登陆,server.js中登陆的路由中一旦用户登陆成功就设置cookie,并将用户的email重要敏感信息明文的展现在cookie中

+++
if(found){
        // Set-Cookie: <cookie-name>=<cookie-value> 
        response.setHeader('set-Cookie',`sign_in_email = ${email}`)
        response.statusCode = 200
      }
+++
复制代码

改一:

咱们不如用一个随机数记录用户的email,setCooke的时候就是展现这个随机数,这样随机数就不容易被篡改了

+++
 if (found) {
        let sessions = {}    
        let sessionID = Math.random() * 1000000
        sessions[sessionID] = { sign_in_email: email }
        response.setHeader('set-Cookie', `sessionID = ${sessionID}`)
        response.statusCode = 200
      }
+++
复制代码

这样登陆成功后,响应头就会有一个set Cookie和对应的随机ID,

咱们该如何利用这个随机的ID呢?

改二:

咱们去看下set Cookie里面的SessionID对应的值是多少

+++
if (path === '/') {
    let string = fs.readFileSync('./index.html', 'utf-8')
    // 读取cookie的email
    let cookies = ''                                  // cookies有可能为空
    if (request.headers.cookie) {
      cookies = request.headers.cookie.split(';')    // ['email = 1@...', 'xxx= 1','yyy=2']
    }
    let hash = {}
    for (let i = 0; i < cookies.length; i++) {
      let parts = cookies[i].split('=')
      let key = parts[0]
      let value = parts[1]
      hash[key] = value
    }
    // 拿到用户cookie里面的sessionID,而后经过sessionID去找sessions这个hash对应的对象的sign_in_email属性对应的值
    let email = sessions[hash.sessionID].sign_in_email  // 这句话要理解
+++ 
复制代码

咱们这样写let email = sessions[hash.sessionID].sign_in_email `会报错,由于sessions是存在内存里的,一刷新就没了,因此要作一下判断

let email = sessions[hash.sessionID].sign_in_email改成 ==>

let sessionId = hash[' sessionID']  // 当心取值时,键名有空格
let mySession = sessions[sessionId]
let email
if (mySession) {
    email = mySession.sign_in_email
}
复制代码

.和[]的区别:

sessions[sessionID] = { sign_in_email: email }
复制代码

sessions.sessionID = { sign_in_email: email }
复制代码

的区别是什么??

JavaScript中对象属性有两种调用方式,一种是点"."一种是"[]"。 通常状况下咱们使用点调用属性的方式,可是当obj的某个属性是一个变量时(你的attr在这里是一个字符串),这种点调用的方式就行不通了,想一想看obj.'property'这样的方式不对; 因此,若是对象内的属性是一个变量,只能使用[]调用

之后能不用.就不用点,而用[]


完成改一和改二处的代码后,重启服务器: node server 8888

而后刷新页面后请求http://localhost:8888/sign_in

而后打开network,sign_in的响应头里有set-Cookie: sessionID = 260634.3102729094

是怎么知道咱们的密码的呢?

是由于咱们在访问首页的时候就带上了Cookie: sessionID=260634.3102729094

服务器就去看内存里面这个ID对应的对象的email是多少,因此服务器就知道用户的身份了,也就是说不告诉浏览器email是多少,只告诉这个email对应的ID是多少,这个ID对应的email服务器是有记录的,就比如是进景区不告诉你票的姓名是什么,只告诉你票对应的ID是多少,这个票的ID对应的姓名不是你关心的,可是景区是有记录每一个ID对应的姓名是什么

就拿去景区买票的例子来讲

一个服务器维护了一个哈希叫sessions,它给每一个用户分配了一小块内存,这个内存用来存储用户的隐私数据,用户值保存这个key也就是随机数,为何是随机数,由于别人很难猜,就篡改不掉cookie了

Session(不翻译)---- 什么是session(session的是指是一块内存)

1.session是基于cookie实现的session必须将SessionID(随机数)经过 Cookie 发给客户端

2.客户端访问服务器时,服务器读取 SessionID

3.服务器有一块内存(哈希表)保存了全部 session

4.经过 SessionID 咱们能够获得对应用户的隐私信息,如 id、email

5.这块内存(哈希表)就是服务器上的全部 session

若是不当心手动将这个随机数删了,请从新登陆,由于这个随机数服务器也没有保存(就比如是把票弄丢了,请从新买票)

编程的经验:一些看起来很复杂的东西,都是由简单的数据结构和简单的交互逻辑实现的,好像不少地方都用到了内存,哈希

JS作的事情:

1.操做dom 2.ajax发请求 3.组织代码,这些已经被vue,react作了

localStorage简介

localStorage是html5提供的API即window.localStorage

localStorage的本质是哈希,Session是服务器上的哈希表,localStorage是浏览器上的哈希表

localStorage如何存值呢?

1.任何一个对象({}).toString()都会变成[object object]

2.只能存字符串,若是存的是对象会自动转成字符串

3.若是真的想存那个对象怎么办呢?只能经过JSON转成字符串

localStorage如何取值呢?

clear以后localStorage就清空了

localStorage用法

变量会消失

咱们在html文件中插入js,在js中写var a = 1 console.log(a).用浏览器在控制台输入a= 2的,这时候a就等于2,可是当咱们刷新页面的时候,这个a是几?当页面刷新以后,这个变量就没了,变成一个新的a ,a = 1.那有什么办法可让变量能记住它上一次的值呢?经过localStorage,它是存在C盘的文件里的,不是存在浏览器上的,因此页面刷新,那个文件仍是一直存在在那里

localStorage让前端有记忆力

例子: 下面这样第一次回去localStorage里找a的值,发现没有a = 0,而后将a做为localStorage的key,a的值做为localStorage里的key对应的值,下一次每次刷新页面,local的a的值都会加1

+++
 <script>
    let a = localStorage.getItem('a')
    if(!a){
        a = 0
    }else{
        a = parseInt(a,10)+1
    }
    console.log('a')
    console.log(a)
    localStorage.setItem('a',a)
    </script>
+++
复制代码

在有localStorage以前全部的变量在页面刷新的那一刻所有销毁不能保存下来,可是有了localStorage能够存在本地的c盘的一个文件里以便之后长期的使用,也就是说在localStorage出来以前前端是没有记忆力的.

localStorage的应用场景

例:若是咱们的网站改版了,要提示用户

alert('您好,咱们的网站改版了,新添了这些功能....')
复制代码

这样每次用户刷新页面都会弹框提示,会很烦,有没有办法只提示一次?

这就须要在跨页面的时候去记一些东西,怎样作呢,不要存到变量里,存到localStorage里

<script>
    let already = localStorage.getItem('已经提示了')
    if(!already){
        alert('您好,网站已经改版了,多了这些功能...')
        localStorage.setItem('已经提示了',true)
    }else{
        
    }
</script>
复制代码

这样第一次进入页面的时候,咱们发现没有提示用户,因此就提示用户,当用户点击肯定以后,localStorage已经将'已经提示了'的key存下来了,第二次刷新页面就不会再刷新页面了,由于咱们知道上一次已经提示过了,这就是localStorage最典型的应用记录下有没有提示过用户

localStorage的特色

1.LocalStorage 跟 HTTP 无关

2.HTTP 不会带上 LocalStorage 的值

3.只有相同域名的页面才能互相读取 LocalStorage(没有同源那么严格)

4.每一个域名 localStorage 最大存储量为 5Mb 左右(每一个浏览器不同)

5.经常使用场景:记录有没有提示过用户(没有用的信息,不能记录密码) LocalStorage 永久有效,除非用户清理缓存

6.理论上localStorage永久有效,除非用户手动清理缓存即在浏览器按住ctrl+shift+deleate设置里的高级里的清除cookie及其余数据,其余数据就包括localStorage的数据

若是不是相同域名都能访问彼此的localStorage那太危险了,若是A网站知道一我的的姓名,邮箱,B网站知道同一我的的出生日期,身份证号,手机号,C网站知道银行卡的帐号,住址之类的用户我的信息,只要这3家网站之间存在彼此相互竞争的关系,就不会拿到用户的完整信息,因此是相对比较安全的

一旦存的数据超过了localStorage的最大容量就会报错

SessionStorage

1,2,3,4,点同localStorage

5.SessionStorage在用户关闭页面后就失效

sessionStorage也是window上的一个属性,API和localStorage同样

sessionStorage和上面说到的session一点关系都没有,sessionStorage叫会话存储而session不翻译

不基于Cookie的session

session能够不基于cookie实现

拿以前写的注册登陆的代码来讲;

当用户登陆成功后不后台不响应cookie而是经过JSON直接返回sessionID传给前端,前端拿到响应后把sessionID后存到localStorage里面备用,而后跳转到首页时候的路径就不是/,而是把sessionStorage写到查询参数里面,怎样知道当前用户的Session呢?经过sessions[query.sessionID],这样首页就能够经过查询参数来知道当前用户的sessionID是什么,这个Session就有用户的email

// server.js
+++
else if (path === '/sign_in' && method === 'POST') {   // 登陆
    +++
    if (found) {
        let sessionID = Math.random() * 1000000
        sessions[sessionID] = {sign_in_email: email }
        // response.setHeader('set-Cookie', `sessionID=${sessionID}`)      第二种方法基于不依赖Cookie的Session
        response.write(`{"sessionID": ${sessionID}}`)
        response.statusCode = 200
    }
   +++
}
+++
复制代码
// sign_in.html
+++
$.post('/sign_in', hash)
    .then((response) => {
        // window.location.href = '/'   第二种方法基于不依赖Cookie的Session
        let obj = JSON.parse(response)
        localStorage.setItem('sessionID',obj.sessionID)
         window.location.href = `/?sessionID=${obj.sessionID}`   // 必定要当心这里的href地址不能有空格否则就找不到这个sessionID
    }, (request) => {
            alert('邮箱与密码不匹配')
        })
+++
复制代码
// server.js 
+++
if (path === '/') {
    let string = fs.readFileSync('./index.html', 'utf-8')
    /*  注释的部分为经过cookie的方法拿到用户的email和password
    let cookies = ''                                  // cookies有可能为空
    if (request.headers.cookie) {
      cookies = request.headers.cookie.split(';')    // ['email = 1@...', 'xxx= 1','yyy=2']
    }
    let hash = {}
    for (let i = 0; i < cookies.length; i++) {
      let parts = cookies[i].split('=')
      let key = parts[0]
      let value = parts[1]
      hash[key] = value
    }
    let sessionId = hash[' sessionID']
    let mySession = sessions[sessionId]
    */
    // 下面的方法是首页经过前端发来的查询参数的sessionID去sessions里面读哪一个session,这个session就有用户的邮箱

    let mySession = sessions[query.sessionID]
    let email
    if (mySession) {
      email = mySession.sign_in_email
    }
+++
复制代码

以上注释的部分为以前是经过基于cookie的Session

Session大部分状况是基于Cookie来存sessionID的,可是也能够经过查询参数和localStorage来存储sessionID

为何会有人拿cookie和localStorage比较呢?

是有历史缘由的,localStorage是新的API,那么以前的程序员是如何作跨页面的存储呢?只能经过cookie, cookie也是存在c盘的一个文件里的,因此Cookie,以后出现了localStorage就不用cookie了,cookie的一个问题就是全部保存在cookie上的数据都会被保存到服务器上 ,这就让请求变慢,服务器性能降低

前端永远不要读写Cookie,那是后端作的事,Cookie通常只存一个sessionID,不存用户的信息,若是想知道用户的数据,向服务器请求

http缓存之缓存控制Cache-Control

如何加缓存

也就是日常说的web性能优化,说的就是如何加速http的请求和响应

那就向以前写的注册登陆的项目里,添加css/default.css和js/main.js,而后在server.js里给他们加路由

而后让default.css和main.js的内容很大很大,发现它们请求的时间很长,那么如何让它们请求的事件加快呢?

在响应的文件路由里添加

response.serHeader('Cache-Control','max-age=30') 
复制代码

在控制台看响应文件的响应头多了'Cache-Control','max-age=30',表示30秒以内就不要再次向服务器发起请求了,当前的已是最新的了.因此该文件的请求时间很短,基本上是0ms.30秒以后再次刷新页面请求的时候,又会从新发起请求可是响应头又多了一个'Cache-Control','max-age=30'再接下来的30秒内不会再向服务器发请求......如此往复

如何更新缓存

为何不少网站首页不容许在响应头里设置Cache-Control?(通常html不设置缓存)

由于若是页面更新了,用户无法获取最新的版本,由于在缓存时间内,用户使用的都是旧的缓存

在设置的缓存时间内加载文件的时候不会再向服务器发请求.那怎么办呢?若是在设置的缓存时间内,服务器更新了这个文件呢?只有请求相同的URL才会利用以前的缓存,若是文件更新了只需在以前的请求路径下加上一个查询参数就好了

如: <link rel="stylesheet"href="./css/default.css?v=2">

这样有什么用呢?还不是在访问这个css,可是URL变了,多了个查询参数,这时候就会重新向服务器发请求,由于URL不是以前的URL了,不是以前的URL就不用以前的缓存了,这样每次升级更新文件的时候,再引入文件的地方修改请求的URL让每次的查询参数不同就能够了.当咱们改了请求的URl时,第一次打开页面会从新发起请求,可是第二次再次打开的时候又会回到缓存控制的状态,继续不向服务器发请求

如:<link rel="stylesheet" href="./css/default.css?v=3">

因此实际中通常'Cache-Control','max-age=xxx'的时间有多长就设置多长,若是文件更新了,只须要修改文件请求时候的URL便可

如:<link rel="stylesheet" href="./css/default.css?v=n">

这样既能保证缓存的事件保持好久,又能随时升级更新文件

Expires

和Cache-Control很像的也是用来控制缓存的,若是同时使用Cache-Control和Expires就会使用Cache-Control,由于Cache-Control是后面才出来的,之前用Expires,如今用Cache-Control

语法:

Expires: <http-date>   // 这里的时间是GMT时间
复制代码

当前的GMT时间

d = new Date()
Wed Jul 31 2019 11:49:12 GMT+0800 (中国标准时间)
d.toGMTString()
"Wed, 31 Jul 2019 03:49:12 GMT"
复制代码

也就是说Expires是设置什么时间过时(设置时间点),Cache-Control(设置时间点)设置的是多长时间后过时

为何尽可能别用Expires呢,由于它的事件是基于本地时间,若是用户本地事件错乱了,那么缓存控制的事件也就还不肯定了

若是Expires和Cache-Control同时设置,那么优先使用Cache-Control,由于Cache-Control是新的API

ETag

先说一下MD5,MD5是一个摘要算法,什么是摘要算法

现实中咱们下载一个文件,咱们怎么知道本身有没有下载对呢?会不会中间除了什么错,致使少了几个字节呢?MD5就是为了解决这个情景生的

好比下载Linux

Linux    400MB     MD5值: xxxx

本身下载的400MB     MD5值: yyyy

理论上2个MD5值要同样才表示下载的Linux是完整的

好比: 在桌面上建立了一个1.txt的文件,文件内容为1111111111111,而后在终端中运行md5sum 1.txt(在Linux系统中)就会显示 f85ed079926039709c8f506a1866c2a9 这样的一串MD5值

咱们再在桌面赋值一份1.txt,文件名叫1-copy.txt,而后进去删掉文件内容的一个1,而后在终端中运行md5sum 1-copy.txt就会显示MD5的值为87b8769b874865e65a4525bfe9e56ba8

先后比较2次的MD5值是不同的因此就意味着下载的文件不是完整的了

MD5和http有什么关系吗?ETag

nodejs也有一个MD5的库,这个库没用过,就用CRM的套路,Google nodejs md5,就会出现npm有一个md5的库

首先安装一下:npm install md5

引入:

var md5 = require('md5');
复制代码

而后在server.js(后端文件)中须要算MD5的路由上,而后把文件的MD5即fileMD5放到ETag上

+++
 if (path === '/js/main.js') {
    let string = fs.readFileSync('/js/main.js', 'utf-8')
    let fileMD5 = md5(string)
    response.setHeader('ETag', fileMD5)
复制代码

当加载main.js的时候,文件的响应头里面就多一个ETag:MD5的值

但是这有什么用呢?当咱们刷新页面的时候,请求头里面多了一个

if-None-Match: ETag上那个MD5的值
复制代码

也就是说他把上一次的MD5值放到下一次请求的if-None-Match里面,if-None-Match里面的MD5值就至关于一个版本号,若是服务器收到这个版本号若是和服务器上的MD5值同样说明不须要下载

+++
 if (path === '/js/main.js') {
    let string = fs.readFileSync('/js/main.js', 'utf-8')
    let fileMD5 = md5(string)
    response.setHeader('ETag', fileMD5)
    if(request.headers['if-None-Match'] === fileMD5){    // 若是同样说明不要从新下载
        response.statusCode = 304                        //   没有响应体只有响应头                        
    }else{
        response.setHeader('ETag', fileMD5)
        response.write(string)                           // 有响应体
    }
     response.end()
+++
复制代码

304表示not Modified,表示没改过

那么使用ETag和使用Cache-Control有什么区别吗?

Cache-Control:表示在缓存时间内若是再次加载相同URL的文件就直接不发起http请求到服务器,从浏览器缓存中拿数据

ETag: 第一次加载文件的时候,会把MD的值放到响应头的ETag里面,当第二次加载文件的时候,页面发起请求时请求头多一个if-None-Match它的值是上一次的MD5的值.服务器收到请求后会和上一次的MD5值进行比较若是同样就没有响应体,给个304

一句话总结区别就是,Cache-Control不发起请求,ETag发请求可是响应体是空的(都是针对请求同上一次同样的文件)

相关文章
相关标签/搜索