前端er应该懂的登陆态:Cookie、Session和Token

简介

你们都知道,HTTP是一个无状态的协议,那么Web应用要怎么保持用户的登陆态呢?前端

若是你对cookiesessiontoken的优缺点不太明白,或者你想知道在实际中到底怎么实现登陆态,那么本文将很是适合你,本文将以发展历程为顺序为你们介绍cookiessession以及token的优点和缺点。node

本文知识点:git

  1. cookiesessiontoken(json web token,jwt)的区别
  2. nodejwt的应用

正文

咱们站在服务器这一端,一个用户请求过来怎么判断他有没有登陆呢?github

在验证用户名和密码以后,咱们能够发给客户端一个凭证(isLogin = true),若是请求中有这个凭证,那么他就是登录以后的用户。 cookiesession的区别在于,凭证的存储位置。换言之,若是凭证存储在客户端,那就是cookie。若是凭证存储在服务端,那就是sessionweb

客户端存储(cookie)

cookie实际上是HTTP头部的一个字段,本质上能够存储任何信息,早年用于实现登陆态,因此有了一层别的含义——客户端存储。把凭证存储到cookie中,每次浏览器的请求会自动带上cookie里的凭证,方便服务端校验,就像下面这样:算法

图1 海绵宝宝请求调用`/login`接口,派大星验证经过后给海绵宝宝颁发的登陆凭证`isLogin=true`express

可是这样面临的问题是:json

用户本人能够经过修改document.cookie="isLogin = true"伪造登录凭证:跨域

图2 海绵宝宝直接修改cookie跳过登陆接口验证获取数据浏览器

服务端存储(session)

session本意是指客户端与服务器的会话状态,因为凭证存储到了服务端,后来也把这些存在服务端的信息称为session

如今服务器决定本身维护登陆状态,仅发给客户端一个key,而后在本身维护一个key-value表,若是请求中有key,而且在表中能够找到对应的value,则视为合法:

图3 海绵宝宝请求调用`/login`接口,派大星验证经过后给海绵宝宝颁发`sessionID`

这样即便海绵宝宝自行修改了sessionID,在派大星那里没有对应的记录,也没法获取数据。

session是一个好的解决方案,可是他的问题是:若是存在多个服务器如负载均衡时,每一个服务器的状态表必须同步,或者抽离出来统一管理,如使用Redis等服务。

Token

还有其余的方法能够实现登录态吗?

cookie方法不须要服务器存储,可是凭证容易被伪造,那有什么办法判断凭证是否伪造呢?

HTTPS同样,咱们可使用签名的方式帮助服务器校验凭证。

JSON Web Token(简称JWT)是以JSON格式存储信息的Token,其结构图以下:

图4 JSON Web Token结构图

JWT由3部分构成:头部,负载和签名。

根据官网介绍

  1. 头部存储Token的类型和签名算法(上图中,类型是jwt,加密算法是HS256
  2. 负载是Token要存储的信息(上图中,存储了用户姓名和昵称信息)
  3. 签名是由指定的算法,将转义后的头部和负载,加上密钥一同加密获得的。

最后将这三部分用.号链接,就能够获得了一个Token了。

使用JWT维护登录态,服务器再也不须要维护状态表,他仅给客户端发送一个加密的数据token,每次请求都带上这个加密的数据,再解密验证是否合法便可。因为是加密的数据,即便用户能够修改,命中概率也很小。

客户端如何存储token呢?

  1. 存在cookie中,虽然设置HttpOnly能够有效防止XSS攻击中token被窃取,可是也就意味着客户端没法获取token来设置CORS头部。
  2. 存在sessionStorage或者localStorage中,能够设置头部解决跨域资源共享问题,同时也能够防止CSRF,可是就须要考虑XSS的问题防止凭证泄露。

NodeJWT的使用

Node中使用JWT只须要两步:

第一步,在你的/login路由中使用jsonwebtoken中间件用于生成token

const jwt = require('jsonwebtoken')
let token = jwt.sign({
      name: user name
    }, config.secret, {
      expiresIn: '24h'
    })
    res.cookie('token', token)

复制代码

具体使用方法请查看jsonwebtokenGithub

第二步,在Node的入口文件app.js中注册express-jwt中间件用于验证token

const expressJwt = require('express-jwt')
app.use(expressJwt({
  secret: config.secret,
  getToken: (req) => {
    return req.cookies.token || null
  }
}).unless({
  path: [
    '/login'
  ]
}))
复制代码

若是getToken返回null,中间件会抛出UnauthorizedError异常:

app.use(function (err, req, res, next) {
  //当token验证失败时会抛出以下错误
  if (err.name === 'UnauthorizedError') {   
      res.status(401).json({
        status: 'fail',
        message: '身份校验过时,请从新登录'
      });
  }
});
复制代码

具体使用语法参考express-jwtGithub

如何实现单点登陆

假设咱们在电脑和手机都使用同一个用户登录,对于服务器来讲,这两次登录生成的token都是合法的,尽管他们是同一个用户。因此两个token不会失效。

要实现单点登录,服务器只须要维护一张userIdtoken之间映射关系的表。每次登录成功都刷新token的值。

在处理业务逻辑以前,使用解密拿到的userId去映射表中找到token,和请求中的token对比就能校验是否合法了。

图5 实现单点登陆

总结

实现登陆态是前端很是基础且重要的技能之一。以前在学习这一块的时候,分不清CookieSessionToken的区别。session是比cookie更好的一种解决方案。token成为主流,是由于他不须要额外的存储管理。可是当涉及到单点登陆的时候,其实也出现了多个服务器须要同步映射表的问题。

欢迎你们在评论区讨论,指正!

参考

  1. 朴灵。《深刻浅出nodejs》。P181
  2. shanyue。jwt 实践以及与 session 对比
相关文章
相关标签/搜索