Cookie、Session、Token(没懂的话再看一遍)

Cooike、Session、Token

接触了这么久的Web,有必要好好总结下这一块了

什么是无状态HTTP协议

在网络没有当今这么发达的时代,Web基本就是用于文档的浏览,作为服务器而言也就是根据相应,加载对应的相应就是了,也就是说不需要记录你干了什么,你有什么,我只知道你请求了,我返回就是了,咱们到此结束就是了。每次请求都会是一次全新的请求。当然没有什么状态的存储一说。
所谓无状态HTTP协议,就是没有保存状态罢了。

会话机制出现

在这么一种没有存储的情况之下,随着网络技术的发展,Web也变得越来越强大,这时候就出现了会话机制。比如说,登录的时候,进行页面的跳转的时候一般都会发起新的请求,但是逻辑是我希望你登录之后才能看见,这时候,该不会让我一直去登录吧。会话机制就是这么的记住了你的状态,然后实现请求变化的时候也保存了状态。

主流的三种会话就是 Cooike、Session、Token

什么是 Cookie

Cookie存在于客户端 ,Cooike是一种存在于客户端的会话机制,由服务端发送到用户的浏览器并保存到本地的一小块数据,在浏览器下一次请求服务端的时候会带上发送给服务端
Cooike是不可以跨域的,每一个Cookie都会绑定一个单一的域名,无法再别的域名下使用,一级域名和二级域名是共享的

查看Cookie,在浏览器的Application中有一个Cooikes的模块存储了该网站的所有的Cooike,当然也可以通过代码document.cookie来获取Cookie的值。
在这里插入图片描述
如图所示就是该网站的所有的Cookie,包含了Cookie的name、Value,Domain(实现跨域)、Expires/maxAge(失效时间和最大的保存时间)、Size(大小,最大为4k),HttpOnly(阻止js脚本获取Cooike信息)、Secure(cookie只能通过https发送,不能通过http发送)

可以看到,Cooike就是这样一个十分具体形象的东西

客户端设置

document.cookie = "name=Indomite;age=12"

客户端可以设置Cookie的选项,但是不能设置 httpOnly ,设置好Cookie之后,Cookie被自动添加到request header中,服务器收到Cooike

服务端的设置
在response的header中有一项叫做set-cooike,专门用于服务端设置Cooike

  • 一个set-cooike只能设置一个cooike,使用多个的时候需要多次使用set-cooike
  • 服务端可以设置cookie的所有选项: expires, domain, path, secure, HttpOnly

在这里插入图片描述

什么是Session

服务端的Session,Session就是存在于服务端的会话,记录客户端与服务端的童话
Session基于Cookie实现,Session存在于服务端,但是需要和客户端进行练习的时候,就必须要通过Cookie了,Session_id会存储在Cookie中
在这里插入图片描述

Session与Cookie的会话流程

  1. 用户在第一次请求服务端的时候,服务端根据用户提交的相关消息创建对应的Session
  2. 服务端接到请求之后响应,返回Session的唯一标识Session_id给客户端,也就是存储在Cookie的Session信息
  3. 客户端接收到服务端发来的Session_id之后,将信息存入Cooike,同时记录好对应的Session_id属于那个域名
  4. 第二次客户端访问服务端的时候,请求会判断时候存在Cookie信息,存在的话直接发给服务端,服务端从Cookie获取Session_id,根据Session_id查找对应的Session信息,没有的话那状态失败,找到之后就可以进行之后的操作了

上述可见,Session_id就是连接Session和Cookie之间的桥梁
Session对应着一台服务器,当存在分布式的时候,服务器之间的Session共享就变得不那么简单了

什么是Token

Token令牌,Token就是访问资源接口(API)时所需要的凭证,在服务端将用户的信息通过Base64编码过后传给客户端,每次请求的时候都带上这一信息,服务端拿到之后解密就知道用户是谁了
Token组成,uid(用户唯一的身份标识)、time(当前的时间戳)、sign(签名、Token前几位以hash算法压缩成的一定长度的字符串)

特点:服务端无状态化、可扩展性强
支持移动端设备
安全、支持跨程序调用
在这里插入图片描述

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端
  4. 客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 token
  6. 服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据

什么是JWT

JSON Web Token,是目前最流行的跨域认证的解决方案
关于JWT的具体参考可以查看阮一峰老师的
在这里插入图片描述

JWT认证流程

  1. 用户输入用户名、密码登录,服务端认证成功之后,或返回客户端一个JWT
  2. 客户端将Token保存到本地(localStorage !、Cookie)
  3. 当用户项访问一个受保护的资源的时候,比如说多权限使用,需要请求Authorization使用Bearer添加JWT
Authorization: Bearer <token>

下面贴一段源码吧

//index.js
const Koa = require('koa')
const Router = require('koa-router')
const bodyParser = require('koa-bodyparser')
const {
    sign
} = require('jsonwebtoken') //签发token
const {
    secret
} = require('./config')
const jwt = require('koa-jwt')({
    secret
}) //jwt加密解密码中间件
const admin = require('./middleware/admin')()

const app = new Koa()
app.use(bodyParser())
const router = new Router()
router.post('/api/login', async (ctx, next) => {
    // ctx.request.params URL请求参数
    // ctx.request.query 请求参数
    // ctx.request.header 头参数
    // ctx.request.body json对象参数

    const user = ctx.request.body;
    if (user && user.username) {
        let {
            username
        } = user
        const token = sign({
            username
        }, secret, {
            expiresIn: 6000
        })
        ctx.body = {
            message: "得到了Token",
            code: 1,
            token
        }
    } else {
        ctx.body = {
            message: "参数错误",
            code: -1
        }
    }
})

router.get('/api/userInfo', jwt, async ctx => {
    ctx.body = {
        message: 'Token 鉴权',
        username: ctx.state.user.username
    }
})

router.get('/api/adminInfo', jwt, admin, async ctx => {
    ctx.body = {
        message: 'Hello Admin',
        username: ctx.state.user.username
    }
})

app.use(router.routes()).use(router.allowedMethods())

app.listen(3000, () => {
    console.log('app listen 3000');
})

//config.js
exports.secret = 'dehiuhsdaf'

//admin.js  中间件
module.exports = () => {
    return async (ctx, next) => {
        if (ctx.state.user.username === 'admin') {
            next()
        } else {
            ctx.body = {
                code: -1,
                message: '用户没有权限'
            }
        }
    }
}
原文地址:https://www.cnblogs.com/Indomite/p/14195222.html