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的会话流程
- 用户在第一次请求服务端的时候,服务端根据用户提交的相关消息创建对应的Session
- 服务端接到请求之后响应,返回Session的唯一标识Session_id给客户端,也就是存储在Cookie的Session信息
- 客户端接收到服务端发来的Session_id之后,将信息存入Cooike,同时记录好对应的Session_id属于那个域名
- 第二次客户端访问服务端的时候,请求会判断时候存在Cookie信息,存在的话直接发给服务端,服务端从Cookie获取Session_id,根据Session_id查找对应的Session信息,没有的话那状态失败,找到之后就可以进行之后的操作了
上述可见,Session_id就是连接Session和Cookie之间的桥梁
Session对应着一台服务器,当存在分布式的时候,服务器之间的Session共享就变得不那么简单了
什么是Token
Token令牌,Token就是访问资源接口(API)时所需要的凭证,在服务端将用户的信息通过Base64编码过后传给客户端,每次请求的时候都带上这一信息,服务端拿到之后解密就知道用户是谁了
Token组成,uid(用户唯一的身份标识)、time(当前的时间戳)、sign(签名、Token前几位以hash算法压缩成的一定长度的字符串)
特点:服务端无状态化、可扩展性强
支持移动端设备
安全、支持跨程序调用
- 客户端使用用户名跟密码请求登录
- 服务端收到请求,去验证用户名与密码
- 验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端
- 客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里
- 客户端每次向服务端请求资源的时候需要带着服务端签发的 token
- 服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据
什么是JWT
JSON Web Token,是目前最流行的跨域认证的解决方案
关于JWT的具体参考可以查看阮一峰老师的
JWT认证流程
- 用户输入用户名、密码登录,服务端认证成功之后,或返回客户端一个JWT
- 客户端将Token保存到本地(localStorage !、Cookie)
- 当用户项访问一个受保护的资源的时候,比如说多权限使用,需要请求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: '用户没有权限'
}
}
}
}