项目地址css
用户能够随意篡改 Cookiehtml
通常来讲,Session 基于 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能够解决这个问题
看下以前的注册登陆,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是html5提供的API即window.localStorage
localStorage的本质是哈希,Session是服务器上的哈希表,localStorage是浏览器上的哈希表
1.任何一个对象({}).toString()
都会变成[object object]
2.只能存字符串,若是存的是对象会自动转成字符串
3.若是真的想存那个对象怎么办呢?只能经过JSON转成字符串
clear以后localStorage就清空了
咱们在html文件中插入js,在js中写var a = 1 console.log(a).用浏览器在控制台输入a= 2的,这时候a就等于2,可是当咱们刷新页面的时候,这个a是几?当页面刷新以后,这个变量就没了,变成一个新的a ,a = 1.那有什么办法可让变量能记住它上一次的值呢?经过localStorage,它是存在C盘的文件里的,不是存在浏览器上的,因此页面刷新,那个文件仍是一直存在在那里
例子: 下面这样第一次回去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出来以前前端是没有记忆力的.
例:若是咱们的网站改版了,要提示用户
alert('您好,咱们的网站改版了,新添了这些功能....')
复制代码
这样每次用户刷新页面都会弹框提示,会很烦,有没有办法只提示一次?
这就须要在跨页面的时候去记一些东西,怎样作呢,不要存到变量里,存到localStorage里
<script>
let already = localStorage.getItem('已经提示了')
if(!already){
alert('您好,网站已经改版了,多了这些功能...')
localStorage.setItem('已经提示了',true)
}else{
}
</script>
复制代码
这样第一次进入页面的时候,咱们发现没有提示用户,因此就提示用户,当用户点击肯定以后,localStorage已经将'已经提示了'的key存下来了,第二次刷新页面就不会再刷新页面了,由于咱们知道上一次已经提示过了,这就是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的最大容量就会报错
1,2,3,4,点同localStorage
5.SessionStorage在用户关闭页面后就失效
sessionStorage也是window上的一个属性,API和localStorage同样
sessionStorage和上面说到的session一点关系都没有,sessionStorage叫会话存储而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
是有历史缘由的,localStorage是新的API,那么以前的程序员是如何作跨页面的存储呢?只能经过cookie, cookie也是存在c盘的一个文件里的,因此Cookie,以后出现了localStorage就不用cookie了,cookie的一个问题就是全部保存在cookie上的数据都会被保存到服务器上 ,这就让请求变慢,服务器性能降低
前端永远不要读写Cookie,那是后端作的事,Cookie通常只存一个sessionID,不存用户的信息,若是想知道用户的数据,向服务器请求
也就是日常说的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">
这样既能保证缓存的事件保持好久,又能随时升级更新文件
和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
先说一下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发请求可是响应体是空的(都是针对请求同上一次同样的文件)