搞懂 Cookie,session与token仅需此篇!

Cookie , Token , Session

Cookie:

  1. 什么是Cookie?

​ Cookie是通过浏览器将服务器返回的数据保存在本地上的一小块数据(一般小于4kb).当浏览器发送请求且浏览器存在Cookie时,浏览器会自动在请求头携带上Cookie数据.引入Cookie的意义是因为HTTP的请求是无状态的,如:浏览器发出的请求服务器没办法区分浏览器用户身份以及用户的相关操作状态(可以通过cookie传递这些信息).

2.Cookie主要使用在一下场景:

会话状态管理(如用户登录状态,及其他需要记录的信息)

个性化设置(如用户自定义设置)

浏览器追踪行为(如追踪分析用户行为)

3.同源限制:

协议相同,域名相同,端口相同

只有同源的网页才能共享Cookie,服务器可以在设置Cookie的时候,指定Cookie的所属域名为一级域名,比如: .example.com.

这样的话,二级域名和三级域名不同做任何设置,都可以读取这个Cookie.

4.创建Cookie:

(1)浏览器自动创建:

当服务器收到HTTP请求时,服务器可以在响应头里设置一个Set-Cookie选项,浏览器接收到响应后会自动保存下Cookie,之后浏览器对该服务器的每一次请求都会通过请求头把Cookie信息发送给服务器.

eg: (nodejs + express):

const express = require('express')
let app = express();
app.get('/',(req,res)=>{
    console.log(req.headers.cookie)
    res.cookie('token','dsafdsfd453542scdsfd')//设置Cookie
    res.send('path is /')
});
app.get('/login',(req,res)=>{
    console.log(req.headers.cookie)
    res.send('path is /login')
});
app.listen(3000,()=>{
    console.log('express服务已经启动~~~');
})

(2).通过JS设置Cookie(也可以设置过期时间,域名,路径等).

Warning: 字符串中不能包含HttpOnly标识.可以包含expires,max-age,domian,path,secure.基本用法:

// 可以直接在浏览器控制台输入进行测试
document.cookie = "token=dsagsdshj43sfs";

5.Cookie的设置详解:

(1)过期时间设置(Expires字段):

设置Cookie 30S 后过期:

// 30s 后浏览器 cookie 自动清空
res.cookie('token','dsafdsfd453542scdsfd',{expires:new Date(Date.now() + 30*1000)})

设置Cookie的过期时间,必须是GMT格式的时间(new Date().toGMTSting() 或 new Date.toTUCString() 获得).

expires=Thu, 25 Feb 2018 04:18:00 GMT 表示 Cookie 在 2018 年 2 月 25 日 4:18 分失效,浏览器会清空失效的 Cookie。

如果没有设置expires,默认有效期为Session,即会话Cookie,关闭浏览器后清空.

expires是http1.0中的属性,在Http1.1中由max-age替代,两种的作用是一样的,只是语法不同.

max-age是以秒为单位时间段,cookie失效时刻 = 创建时刻 + max-age,也就是存货多少秒.默认值为 -1 , 表示浏览器关闭就删除的会话Cookie , 0 表示马上失效(删除).

(2) . 设置域(domain),指定cookie在哪个域下可以被接收:

Warning: 如果不指定domian则默认是当源origin,但不包括子域.如果指定了Domian则一般包含子域(二级子域,三级子域).

res.cookie('token','dsafdsfd453542scdsfd',{domain:'localhost'})

假设有一个domian为baidu.com的Cookie.若请求的域名是baidu.com,api.baidu.com都会发送该Cookie(一级域名含二级域名).如果请求google.com就不会发送这个Cookie.

如果是跨域XHR请求,即使domain和path都满足Cookie的domain和path,默认情况下Cookie也不会添加到请求头中.

domian可以设置为页面本域或父域,例如www.baidu.com可以设置www.baidu.com和baidu.com这两个域.

(3).设置路劲(path),指定cookie在当前主机下那些路径可以接受Cookie.

设置在/login下接受Cookie.(只要是/login开头的都能接受,如:/login/user等),此时"/"下是没有Cookie的.

res.cookie('token','dsafdsfd453542scdsfd',{path:'/login'})

/表示所有路劲. path的默认值为设置该Cookie的网页所在目录.

(4) 设置不能通过javascript访问cookie. (HttpOnly字段)

不能通过document.cookie访问.

res.cookie('token','dsafdsfd453542scdsfd',{httpOnly:true})

默认情况下,Cookie不会带HttpOnly选项.可以通过JS读取,修改,删除Cookie.

(5) 设置secure字段:

标记为secure的Cookie只能通过被Https协议加密过的请求发送给给服务端.(通过https创建的Cookie只能通过Https请求将Cookie携带到服务器,通过http无法拿到Cookie).

res.cookie('token','dsafdsfd453542scdsfd',{secure:true}

(6)设置 someSite ,它有三个可选值 None、strict、Lax

首先了解下什么是跨站请求?比如说有 A、B两个网站,其中A站请求会产生 Cookie,且后续访问请求需携带回 Cookie(身份认证),如果在B网站通过链接的形式访问A站资源这个就叫跨站。这种情况访问成功与否会根据 Cookie 设置的 someSite 而定。

  • someSize:None: 浏览器在同站请求、跨站请求下都会发送 Cookies
  • someSize:Strict: 浏览器只会在相同站点下发送 Cookies
  • someSize:Lax: 与 strict 类似,不同的是它可以从外站通过链接导航到该站。
  1. 通过JS访问Cookie :

    设置了HttpOnly标志的Cookie无法访问.

   document.cookie
  1. 跨域

​ 从安全角度考虑,浏览器无法获取跨域的Cookie是不会变的,跨域携带Cookie只有两种方法:

nginx 转发到同一个域名.

前后台设置withCredentials , Access-Control-Allow-Credentials

默认情况下浏览器对跨域请求不会携带Cookie,但鉴于Cookie在身份验证等方面的重要性,CORS推荐使用额外的响应头字段来允许跨域发送Cookie.

在open XMLHttpRequest之后,设置withCredentials = true 可让该跨域请求携带Cookie (携带的是目标页面所在域的Cookie)

var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.withCredentials = true;
xhr.send();

Access-Control-Allow-Credentials

只设置客户端当然是没用的,还需要目标服务器接受让你跨域发送的Cookie.否则会被浏览器的同源策略挡住:

服务器同时设置Access-Control-Allow-Credentials 响应头为"true" , 即可允许跨域请求携带Cookie.

Access-Control-Allow-Origin

除了设置Access - Control - Allow - Credentials 之外,跨域发送Cookie还要球Access-Control-Allow-Origin不允许使用通配符 * . 否则,浏览器还是会组织跨域请求.

小结: Cookie更多的用途使用在身份认证来保证网站资源的安全性,而不是大数据量的本地数据存储.大数据量本地存储方案取而代之的是 webstorage 和 indexedDB.设置Cookie过期时间有助于防止会话固定攻击.设置HttpOnly有助于跨站点脚本(xss)攻击.设置SameSite可以阻止跨站请求伪造攻击(CSRF).

Session 介绍:

(1) Web中什么是会话 ?

用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话.

(2) 什么是Session ?

Session : 在计算机中,尤其是在网络应用中,称之为"会话控制" .Session 对象存储特定用户会话所需的属性及配置信息.

(3)Session什么时候产生?

当用户请求来自应用程序的web页是,如果该用户还没有会话,则Web服务器将自动创建一个Session对象.

这样,当用户在应用程序的Web页之间跳转时,存储在Session对象中的变量将不会丢失,而是在整个用户会话中一直存下去.

服务器会向客户浏览器发送一个每个用户特有的会话编号sessionID , 让它进入到cookie里.

服务器同时也把sessionID和对应的用户信息,用户操作记录在服务器上,这些记录就是session.再次访问时会发送cookie给服务器,其中就包括sessionID.

服务器从cookie里找到sessionID,再根据sessionID找到以前记录的用户信息就可以直到他之前操控,访问过哪里.

(4) Session的生命周期 :

一般来说,半小时.举个例子,你登录一个服务器,服务器返回给你一个sessionID,登录成功之后的半小时之内没有对该服务器进行任何HTTP请求,半小时后你进行一次HTTP请求,会提示你重新登录。

小结:

Session 是另一个记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上.客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上.这就是Session.客户端浏览器再次访问时只需要从Session中查找该用户的状态就可以了.

在Nodejs中如何使用Session ?

利用express - session 插件:

常用参数:

secret : 一个String类型的字符串,作为服务端生成session的签名.

name: 返回客户端的key名称,默认为connect.sid,也可以自己设置.

resave : (是否允许)当前客户端并行发送多个请求时,其中一个请求在另一个请求在另一个请求结束时对session进行修改覆盖并保存. 默认为true . 但是(后续版本)可能默认失效,所以最好手动添加.

saveUninitialized:初始化session时是否保存到存储。默认为true, 但是(后续版本)有可能默认失效,所以最好手动添加。

  **cookie**:设置返回到前端key的属性,默认值为{ path: '/', httpOnly: true, secure: false, maxAge: null }。 

express-session的一些方法:

Session.destroy():删除session,当检测到客户端关闭时调用.

Session.reload(): 当session有修改时,刷新session.

Session.regenerate() : 将已有session初始化.

Session.save() : 保存session.

第一次访问http://localhost:3000/users时,会提示没有登录,访问http://localhost:3000/ 后再访问,则显示用户信息.

app.js:

const express = require('express');
const cookieParser = require('cookie-parser');
const session = require('express-session');

const app = express();

app.use(session({
    secret: 'keyboard cat',
    resave: false,
    saveUninitialized: true,
    cookie: { secure: false }
}));

//第一次访问home展示还未登录,通过login登录获取session才会登录
app.use('/home',(req,res,next)=>{
    if(req.session.user){
        var user=req.session.user;
        var name=user.name;
        res.send('你好'+name+',欢迎登录。');
        next();
    }else{
        res.send('你还没有登录,先登录下再试试!');
        next();
    }
})

//为用户设置session
app.get('/login', function(req, res, next) {
    var user={
          name:"david",
          age:"22"
    }
    req.session.user=user;
    res.send('恭喜您成功获取session!');
    next();
});





app.listen(3000);

session作为一种较为安全的会话控制,一般来讲时不允许前端来获取的,所以这里就不演示通过js获取session的方法了.

JSON Web Tkoen

JSON Web Token (缩写JWT) 是目前最流行的跨域认证解决方案.

一.跨域认证的问题

互联网服务离不开用户认证.一般流程是下面这样:

  1. 用户向服务器发送用户名和密码.
  2. 服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色,登录时间等等.
  3. 服务器向永辉返回一个session_id,写入用户的Cookie.
  4. 用户随后的每一次请求,都会通过Cookie,将session_id传回服务器.
  5. 服务器收到session_id,找到前期保存的数据,由此得知用户的身份.

这种模式的问题在于,扩展性不好.单机当然没有问题,如果是服务器集群,或者是跨域的服务架构,就要求session数据共享,每台服务器都能够读取session.

举例来说,A网站和B网站是同一家公司的关联服务.现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现?

一种解决方案是session数据持久化,写入数据库或别的持久层.各种服务收到请求后,都向持久层请求数据.这种方案的优点是架构清晰,缺点是工程量比较大.另外,持久层万一挂了,就会单点失败.

另一种方案是服务器索性不保存session数据了,所有数据都保存在客户端,每次请求都发回服务器.JWT就是这种方案的一个代表.

JWT的原理

JWT的原理是,服务器认证后,生成一个JSON对象,发回给用户.eg:

{
    "姓名":"张三",
    "角色":"管理员",
    "到期时间":"2018年7月1日0点0分"
}

以后,用户域服务端通信的时候,都要发回这个JSON对象.服务器完全只靠这个对象认定用户身份.为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名.

服务器就不保存任何session数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展.

JWT的数据结构

它是一个很长的字符串,中间用点(.)分割成三个部分.注意,JWT内部是没有换行的,这里只是为了便于展示,将它写成了几行.

JWT的三个部分依次如下.

Header (头部)

Payload (负载)

Signature (签名)

写成一行,就是 Header , Payload , Signnature.

(1) Header:

Header部分是一个JSON对象,描述JWT的元数据,通常是下面的样子.

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

上面代码中,alg属性表示签名的算法,默认是HMAC SHA256(写成HS256); typ属性表示这个令牌(token)的类型(type),JWT令牌统一写为JWT.

最后,将上面的JSON对象使用Base64URL算法转成字符串.

(2) Payload:

Payload部分也是一个JSON对象,用来存放需要传递的数据.JWT规定了7个官方字段,供选用.

iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号

除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子.

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

注意,JWT默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分.

这个JSON对象也要使用Base64URL算法转成字符串.

(3) Signature

Signature 部分是对前面两部分的签名,防止数据篡改.

首先,需要指定一个密钥(secret).这个密钥只有服务器才知道,不能泄露给用户.然后,使用Header里面指定的签名算法(默认是HMAC SHA256),按照下面的公式产生签名.

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

算出签名以后,把Header,Playload,Signature三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户.

(4) Base64URL

前面提到过,Header和Payload串型化的算法是 Base64URL。这个算法跟Base64算法基本类似,但有一些小的不同.

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+/=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-/替换成_ 。这就是 Base64URL 算法。

(5)JWT的几个特点:

  1. JWT默认是不加密的,但也可以加密.生成原始TOKEN以后,可以用密钥再加密一次.
  2. JWT不加密的情况下,不能将私密数据写入JWT.
  3. JWT不仅可以用于认证,也可以用于交换信息.有效使用JWT,可以降低服务器查询数据库的次数.
  4. JWT的最大缺点是,由于服务器不把偶才能session状态,因此无法在使用过程中废止某个token,或者更改token的权限.也就是说,一旦JWT签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑.
  5. JWT本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限.为了减少盗用,JWT的有效期应该设置得比较短.对于一些比较重要得权限,使用是应该再次对用户进行认证.
  6. 为了减少盗用,JWT不应该使用HTTP协议明码传输,要使用HTTPS协议传输.

Nodejs中关于Token得使用:

通过npm包jsonwebToken来完成token的生成和验证.

npm install --save jsonwebtoken

index.js:

const express = require("express")
const app = express()
const jwt = require("jsonwebtoken")
//撒盐,加密时候混淆
const secret = '113Bmongojsdalkfnxcvmas'
const user = {
	id:10,
	name:"Bmongo",
	age:16,
}

//生成token
//info也就是payload是需要存入token的信息
function createToken(info) {
	let token = jwt.sign(info, secret, {
        //Token有效时间 单位s
		expiresIn:60 * 60 * 10
	})
	return token
}

//验证Token
function verifyToken(token) {
	return new Promise((resolve, reject) => {
		jwt.verify(token, secret, (error, result) => {
            if(error){
                reject(error)
            } else {
                resolve(result)
            }
		})
	})
}

//设置允许跨域
app.use(function(req, res, next) {
    //指定允许其他域名访问 *所有
	res.setHeader("Access-Control-Allow-Origin", "*");
    //允许客户端请求头中带有的
	res.setHeader("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
    //允许请求的类型
	res.setHeader("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
	res.setHeader("X-Powered-By",' 3.2.1')
    //让options请求快速返回
	if(req.method=="OPTIONS") res.send(200);
	else  next();
});

//白名单
const whiteList = ['/login']

app.use((req,res,next) => {
    console.log(JSON.stringify(req.headers.authorization))
	if(!whiteList.includes(req.url)) {
		verifyToken(req.headers.authorization).then(res => {
			next();
		}).catch(e => {
			res.status(401).send('您还未登录,请您先登录!')
		})
	} else {
		next();
	}
})

app.use('/login',(req,res) => {
	let token = createToken(user);
	res.json({token})
})

app.get("/api/info", (req,res) => {
	res.send({
		result:1,
		data:{
            "name":"Bmongo",
            "id":1
        }
	})
})

app.listen(3000);

前端代码:

<script>
        let xhr = new XMLHttpRequest();
        xhr.onreadystatechange = ()=>{
            if(xhr.readyState === 4){
                if(xhr.status >=200 && xhr.status<300){
                   console.log(JSON.parse(xhr.responseText));
                   sessionStorage.setItem('token',JSON.parse(xhr.responseText).token)
                   
                }
            }
        }
       
        xhr.open('GET','http://localhost:3000/api/info');
        xhr.setRequestHeader('Authorization',sessionStorage.getItem('token'));
        xhr.send();
    </script>

Cookie,session与token的区别

Cookie实际上是一小段的文本信息。 Cookies是由服务器产生的。 当浏览器第一次访问服务端时,服务器此时肯定不知道他的身份,所以创建一个独特的身份标识数据,格式为key=value,放入到Set-Cookie字段里,随着响应报文发给浏览器。浏览器看到有Set-Cookie字段以后就知道这是服务器给的身份标识,于是就保存起来,下次请求时会自动将此key=value值放入到Cookie字段中发给服务端。服务端收到请求报文后,发现Cookie字段中有值,就能根据此值识别用户的身份然后提供数据。

Session机制是一种服务端的机制 ,服务器使用一种类似散列表的结构来保存信息。当程序需要为某个客户端的请求创建一个session的时候 ,服务器首先检查这个客户端里的请求里是否已包含了一个session标识-session ID ,如果已经包含一个session ID ,则说明已经为此客户端创建过session,服务器就按照session ID把这个session检索出来使用。如果客户端请求不包含session lD,则为此客户端创建一个session并且声称一个与此session相关联的session ID ,session ID的值应该是一个既不会重复 ,又不容易被找到规律以仿造的字符串(服务器会自动创建),这个session ID将被在本次响应中返回给客户端保存。

Cookie和session的区别

1.cookie数据存放在客户的浏览器上,session数据放在服务器上.

2.cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗.

3.session会在一定时间内保存在服务器上,当访问增多,会比较占用服务器的性能.

4.单个cookie保存的数据不超过4k,很多浏览器都限制一个站点最多保存20个cookie.

所以 : 将登录信息等重要信息存放在cookie. 其他信息如果需要保留,可以存放在cookie中.

Token是在服务端将用户信息经过Base64Url编码过后传给在客户端,每次用户请求的时候都会带上这一段信息,因此服务端拿到此信息进行解密后就知道此用户是谁了,这个方法叫做JWT(JSON Web Token)。 Token类似一个令牌,无状态的,服务端所需的信息被Base64编码后放到Token中,服务器可以直接解码出其中的数据。

Token相比较于Session的优点在于,当后端系统有多台时,由于是客户端访问时直接带着数据,因此无需做共享数据的操作。

Token的优点:

简洁:可以通过URL,POST参数或者是在HTTP头参数发送,因为数据量小,传输速度也很快

自包含:由于字符串包含了用户所需要的信息,避免了多次查询数据库,因为Token是以JSON的形式保存在客户端的,所以JWT是跨语言的,不需要在服务端保存会话信息,特别适用于分布式微服务 .session 和 token并不矛盾,作为身份认证 token安全性比session好,因为每个请求都有签名还能防止监听以及重放攻击,而session就必须靠链路层来保障通讯安全了。如上所说,如果你需要实现有状态的会话,仍然可以增加session来在服务器端保存一些状态

token与cookie的区别:

Cookie是不允许垮域访问的,但是token是支持的, 前提是传输的用户认证信息通过HTTP头传输;

总结:

token就是令牌,比如你授权(登录)一个程序时,他就是个依据,判断你是否已经授权该网站;cookie就是写在客户端的一个txt文件,里面包括你登录信息之类的,这样你下次在登录某个网站,就会自动调用cookie自动登录用户名;session和cookie差不多,只是session是写在服务器端的文件,也需要在客户端写入cookie文件,但是文件里是你的浏览器编号Session的状态是存储在服务器端,客户端只有session id;

而Token的状态是存储在客户端。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值