前端关于存储


本地存储

使用HTML5可以在本地存储用户的浏览数据:local Storage、session Storage

1.数据存储于自己的浏览器当中

2.设置、读取十分方便,页面刷新数据不丢失

3.容量较大,sessionStorage约5M,localStorage约20M(可是我测试得到我chrome浏览器localStorage才5M)

4.只能存储字符串,可以将对象JSON.stringify() 编码后存储

cookie是网站为了标示用户身份而储存在用户本地终端上的数据;cookie数据始终在同源的http请求中携带(即使不需要),也会在浏览器和服务器间来回传递。
sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存;

5.此外,User Data微软专门为IE在系统中开辟的一块存储空间,单个文件限制大小128kb,一个域名下可保存1M

window.sessionStorage

1.生命周期为关闭浏览器窗口

2.同一个窗口下数据可以共享

3.以键值对的形式存储使用

window.sessionStorage.setItem(key, value) 分别对应键和值,设置(添加或更改)

window.sessionStorage.getItem('key') 获取

window.sessionStorage.removeItem('key') 删除

window.sessionStorage.clear() 删除所有数据(慎用),不在Firefox中实现

4.所有现代浏览器在实现存储写入时都使用了同步阻塞方式,因此数据会被立即提交到存储。(除了老版IE)

window.localStorage

1.生命周期永久生效,除非手动删除,不然关闭页面仍然存在

2.多窗口共享(同一浏览器才能共享)

3.以键值对的形式存储使用

window.localStorage.setItem(key, value) 分别对应键和值,设置(添加或更改)(值是以字符串的形式进行存储的)

window.localStorage.getItem('key') 获取

window.localStorage.removeItem('key') 删除

window.localStorage.clear() 删除所有数据(慎用),不在Firefox中实现

Cookie是一种在客户端保持HTTP状态信息的技术:http协议是无状态的,但是在实际工作中,一些万维网网站希望能识别用户(给用户推销产品)=>诞生了cookie小饼干(由服务器生成)

cookie是存储在用户主机的文本文件,存储在浏览器中的纯文本,浏览器的安装目录下会专门有一个 cookie 文件夹来存放各个域下设置的cookie,记录一段时间内某用户的访问记录

  • 一旦浏览器保存了某个Cookie,那么以后每次访问服务器时,都会在HTTP请求头中将这个Cookie回传给服务器,所以我们前端这里几乎无需什么操作,几乎都是后端那边在操作

  • cookie数据大小不能超过4k

    持久化cookie设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭

    但是如果cookie不包含到期日期(Expires/Max-Age),则可视为会话cookie,它在浏览器关闭后就消失了

  • Web服务器怎么给客户端发Cookie的呢?服务器通过在HTTP响应头中增加Set-Cookie:字段,而浏览器则通过HTTP请求头增加Cookie字段回传

工作中识别用户给用户推销产品,用来保存一些不太敏感的数据(但我很纳闷,其实很多用户用cookie来记住密码,不过我看到一个答案就是:chrome是加密的,只是在密码管理里面明文显示而已。它是使用你Windows的登录密码加密的,还是比较安全的。)

tips:一个浏览器针对一个网站最多存 20 个Cookie,浏览器一般只允许存放 300 个Cookie
移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token

Cookie属性

domain是域名

path是路径

两者加起来就构成了 URL,domainpath一起来限制 cookie 能被哪些 URL 访问。

某cookie的 domain为“baidu.com”, path为“/ ”,若请求的URL(URL 可以是js/html/img/css资源请求,但不包括 XHR 请求)的域名是“baidu.com”或其子域如“api.baidu.com”、“dev.api.baidu.com”,且 URL 的路径是“/ ”或子路径“/home”、“/home/login”,则浏览器会将此 cookie 添加到该请求的 cookie 头部中。

domainpath2个选项共同决定了cookie何时被浏览器自动添加到请求头部中发送出去。如果没有设置这两个选项,则会使用默认值

secure属性

secure 选项用来设置cookie只在确保安全的请求中才会发送。当请求是HTTPS或者其他安全协议时,包含 secure 选项的 cookie 才能被发送至服务器。

设置cookie

服务端设置cookie

cookie和session成对配合 + 生成

set-cookie字段,当你要想设置多个 cookie,需要添加同样多的set-Cookie字段。

  • 服务端可以设置cookie 的所有选项:expiresdomainpathsecureHttpOnly

客户端设置cookie

document.cookie = "name=Jonh";
document.cookie = "age=12";
document.cookie = "class=111";

但是红宝书推荐使用 encodeURIComponent 对名称和值进行编码

document.cookie = encodeURIComponent("name") + "=" +  encodeURIComponent("Jonh");

encodeURIComponent()函数通过将一个,两个,三个或四个表示字符的UTF-8编码的转义序列替换某些字符的每个实例来编码 URI (对于由两个“代理”字符组成的字符而言,将仅是四个转义序列) 。

// encodes characters such as ?,=,/,&,:
console.log(`?x=${encodeURIComponent('test?')}`);
// expected output: "?x=test%3F"

console.log(`?x=${encodeURIComponent('шеллы')}`);
// expected output: "?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"

decodeURIComponent() 方法用于解码由 encodeURIComponent 方法或者其它类似方法编码的部分统一资源标识符(URI)

Session

cookie和session都是用来跟踪浏览器用户身份的会话方式,通常使用cookie方式存储session ID到客户端

如果服务端直接是把用户信息放在cookie,并且每一次从客户端传过来使用的话,如果客户端偷偷更改数据,比如用户名,其实不是乱套了?所以服务端 不能 直接拿cookie来保存用户信息,然后直接来使用(也就是nodejs通过 res.cookie使用 ),所以存session id最稳妥

现在大多都是Session + Cookie,但是只用session不用cookie,或是只用cookie,不用session在理论上都可以保持会话状态。可是实际中因为多种原因,一般不会单独使用

保持状态:cookie保存在浏览器端,session保存在服务器端

存储的大小:cookie:单个cookie保存的数据不能超过4kb;session大小没有限制。

安全性:cookie:针对cookie所存在的攻击:Cookie欺骗,Cookie截获;session的安全性大于cookie。

应用

  • cookie判断用户是否登陆过网站,以便下次登录时能够实现自动登录(或者记住密码);保存上次登录的时间等信息。

  • Session用于保存每个用户的专用信息,变量的值保存在服务器端,服务端会从请求传回来的 Cookie 中获取 SessionID,通过SessionID来区分不同的客户。

  (1)网上商城中的购物车

  (2)保存用户登录信息

  (3)将某些数据放入session中,供同一用户的不同页面使用

  (4)防止用户非法登录

cookie - session认证流程

1、用户向服务器发送用户名和密码。

2、服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。

3、服务器向用户返回一个 session_id,写入 Cookie,(有时为了安全,分为多个cookie)传给客户端。

4、用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。

5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。

session缺点: Session保存的东西越多,就越占用服务器内存,对于用户在线人数较多的网站,服务器的内存压力会比较大。

session持久化

由于session是存储在服务器的内存中,如果服务器突然崩了,或者服务器关闭,那岂不是“七天免登录”存储功能失效了?

此时服务端不能单单保存在服务器内存里,还要存到数据库中

session弊端

当前登陆账号成功之后,别人只要拿到你的cookie(可以通过chrome插件一键导出cookie),然后别人就可以直接伪造你的身份直接拿cookie登录,所以在公共电脑,离开之后必须记得点击退出当前帐号,此时服务器就会销毁当前session

Token

上述展示了基于服务器验证的session(sessionID)暴露了一些弊端,当然使用session还有可能引起:跨域共享问题、CSRF攻击问题和可拓展性问题。

基于 Token 的身份验证是无状态的,我们不用将用户信息存在服务器或 Session 中。这种概念解决了在服务端存储信息时的许多问题。没有 session 信息意味着你的程序可以根据需要去增减机器,而不用去担心用户是否登录和已经登录到了哪里。

大多的token验证流程如下:

  1. 用户通过用户名和密码发送请求。
  2. 程序验证。
  3. 程序返回一个签名的 token 给客户端(令牌)。
  4. 客户端储存 token, 并且每次请求都会附带它。
  5. 服务端验证 token 并返回数据。(服务器端采用filter过滤器校验。校验成功则返回请求数据,校验失败则返回错误码)

token优势

  • 无状态(实际上是来自于JWT),无需存储session信息,负载均衡服务器,能够将用户请求传递到任何一台服务器上

  • 可拓展,token保存在客户端本地,使用token可以和其他应用共享权限,比如将博客账号和qq账号进行关联,当通过第三方平台登录qq时,我们可以把博客发到qq平台中

  • 安全性,防止CSRF攻击,请求中发送的是token而不是cookie,浏览器会将接收到的token值存储在Local Storage、sessionStorage或者cookie中

    • 根据浏览器策略,通过domain和path传递cookie是浏览器行为(非人为控制),而token的获取需要通过js脚本控制,然后根据同源策略,b网站也只能拿到b网站的Local Storage、sessionStorage或者cookie中

    • 在信任网站的HTML或js中,会向服务器传递参数token,不是通过Cookie传递的,若恶意网站要伪造用户的请求,也必须伪造这个token,否则用户身份验证不通过。

    • 即使token存储再cookie中,同源策略限制了恶意网站不能拿到信任网站的Cookie内容(token的生成需要提取Cookie里面的内容进行生成),只能使用(只能携带,不能拿出来看!!),所以就算是token是存放在Cookie中的,恶意网站也无法提取出Cookie中的token数据进行伪造。也就无法传递正确的token给服务器,进而无法成功伪装成用户了。

  • 移动端上不支持 cookie,而 token 只要客户端能够进行存储就能够使用,因此 token 在移动端上也具有优势。

不过,token有过期时间,超过事件后要重新获取token

Token 的种类
一般来说 token 主要三种:

  • 自定义的 token:开发者根据业务逻辑自定义的 token
  • JWT:JSON Web Token,定义在 RFC 7519 中的一种 token 规范
  • Oauth2.0:定义在 RFC 6750 中的一种授权规范,但这其实并不是一种 token,只是其中也有用到 token

附上登录认证的进化路程参考网址https://www.cnblogs.com/fengzheng/p/8416393.html

(传统cookie-Session -> 改造版cookie-session -> 基于JWT的Token认证 -> OAuth认证)

拦截器定义携带token

本例使用axios带的请求拦截器,拦截请求,并且附带上token

import MYRequest from './request'
import { BASE_URL } from './request/config'
const myRequest = new MYRequest({
  baseURL: BASE_URL,
  interceptors: {
    requesInterceptor: (config) => {
      const token = '123'
      if (token && config.headers) {
        config.headers.Authorization = `Bearer ${token}`
      }
      console.log('请求成功拦截', config)
      return config
    },
    requstInterceptorCatch: (err) => {
      console.log('请求失败拦截', err)
    },
    responseInterceptor: (config) => {
      // console.log('响应成功拦截', config)
      return config
    }
  }
})
export default myRequest

内部上有:

this.instance.interceptors.request.use(
    this.interceptors?.requesInterceptor,
    this.interceptors?.requstInterceptorCatch
)

JWT 的组成和优势

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

一个JWT实际上就是一个字符串,它由三部分组成,头部载荷签名。中间用 . 分隔,例如:xxxxx.yyyyy.zzzzz

头部(Header)

头部用于描述关于该JWT的最基本的信息

{
  "typ": "JWT",
  "alg": "HS256"
}

说明了这是一个JWT,并且我们所用的签名算法(后面会提到)是HS256算法

载荷(Payload)

载荷中放置了 token 的一些基本信息,以帮助接受它的服务器来理解这个 token。同时还可以包含一些自定义的信息,用户信息交换。

Payload 部分也是一个 JSON 对象,JWT 规定了7个官方字段,供选用。

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号
{
    "iss": "John Wu JWT",         //签发者
    "iat": 1441593502,            //签发时间
    "exp": 1441594722,            //过期时间
    "aud": "www.example.com",     //受众
    "sub": "jrocket@example.com", //主题
    "from_user": "B",             //自定义私有字段
    "target_user": "A"            //自定义私有字段
}

通过Node.js的包base64url来得到一个很长的字符串( console.log(base64url(JSON.stringify(header))) ),如下图

签名(Signature)

为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名

签名时需要用到前面编码过的两个字符串,使用 Header 里面指定的签名算法(默认是 HMAC SHA256)

加密算法对于不同的输入(头部和载荷)产生的输出总是不一样的,如果数据被修改,得到的签名总会是不一样的,而且,如果不知道服务器加密的时候使用密钥,得到的签名也会是不一样的。

如果服务器应用对头部和载荷再次以同样方法签名之后发现,自己计算出来的签名和接受到的签名不一样,那么就说明这个Token的内容被别人动过的,我们应该拒绝这个Token,返回一个HTTP 401 Unauthorized响应。

三者组合就形成了一个完整的JWT

建议

  • JWT中,不应该在载荷里面加入任何敏感的数据(比如密码),怀有恶意的第三方通过Base64解码可以获取到JWT里的信息
  • 虽说JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。(此时才可以写入秘密数据)
  • 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。
  • JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

使用场景

  • 完成加好友的操作,还有诸如下订单的操作
  • 设计用户认证和授权系统,甚至实现Web应用的单点登录。

JWT和token的不同点

Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效。

JWT:将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据。

JWT参考文章:http://blog.leapoahead.com/2015/09/06/understanding-jwt/

阮一峰老师的介绍 http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

JWT详解攻略:https://learnku.com/articles/17883?order_by=vote_count&

JWT实战演练

先进行安装

npm i jsonwebtoken
npm i express-jwt

jsonwebtoken: 用于生成 Token 。它也有解析 Token 的功能

express-jwt: 用于解析 Token(比 jsonwebtoken 解决方便) , 它把解析之后的数据,存放到 requset.user

jsonwebtoken 提供了sign(payload, secretOrPrivateKey, [options, callback])的方法。

  • sign :对应的其实就是 JWT 签名(Signature)的动作
  • payload:对应的是传入的载荷,可以省略官方提供七个字段,额外添加自定义私有字段,比如用户信息userId等等
  • secretOrPrivateKey:自定义的密钥,属于敏感信息
  • options:可以配置 header 、荷载、指定算法类型(常用的只有 exp(expiresIn)有效时间 和 algorithm 算法类型这两个字段)

参考文章https://juejin.cn/post/6932374305758167054


文章作者: Hello
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Hello !
 上一篇
JQuery JQuery
1.JQuery概述JavaScript库:即library,是一个封装好的特定集合(方法和函数),使得我们可以快速高效的使用这些封装好的功能,而jQuery 就是一个 JavaScript函数库 其他常见地js库还有Prototype、Y
2020-12-24
下一篇 
PC&移动端网页特效(JS) PC&移动端网页特效(JS)
PC端1.offset元素偏移量我们使用offset系列相关属性可以动态获取元素的位置(大小)(父亲要定位此功能才有用 element.offsetTop 返回元素相对带有定位父元素上方的偏移,不带单位,如果没有定位或者没有父元素则以bo
2020-11-18
  目录