一文搞懂Cookie+Session,Redis+Token,JWT三者的区别

本文图片部分来自网络

一、基于Cookie+Seesion传统的认证机制

1.认证流程

http是无状态协议,比如客户端发送了提交一个用户名和密码的请求进行用户认证,那么当页面关闭后,下一次用户还得再发送一次请求来进行用户认证。

因为http协议是不知道是哪个用户发送的请求,所以只能在服务器上存储一份用户的信息(Session),这份登录信息会在服务器响应的时候发送一个(Sessionid)保存到Cookie中,下次客户端发送请求的时候把这个Sessionid放到Cookie中带给服务器,就可以让服务器识别出我们的请求来自于哪个用户了,这就是传统的基于Cookie+Session的认证机制

在这里插入图片描述

2.暴露问题

2.1服务器开销增大

因为每一个访问请求都会创建一个Session对象,如果当用户数量过多的时候服务器就会放过多的Session,导致服务器开销增大

2.2负载均衡问题

当用户认证成功后,Session对象还保存在服务器内存中,这意味着用户下次的请求必须还是在这一台服务器上,这样才能拿到授权的资源。比如说某个客户端有可能第一次认证的时候session存在了服务器1,但是第二次session被分配到了服务器2,就会找不到session导致后续功能失效。

当然这个问题的解决方法有很多:

1.session复制,每次复制session到每一个服务器中。数据冗余。

2.使用NoSQL数据库,第一次请求服务器的时候把session存放在NoSQL数据库中,以后其他服务器访问的时候,先去NoSQL数据库中寻找是否有session。完全在内存中,速度快,数据结构简单,减少了CPU的压力(但是有个问题NoSQL数据库也是放在服务器上的,如果NoSQL数据库挂掉了,那么所有的用户都需要重新认证,这样并不是很完美)

2.3CSRF(Cross Site Request Forgery)跨站域请求伪造攻击

因为Sessionid是放在Cookie中的,如果Cookie被截获,用户就很容易遭到CSRF攻击

比如:

如果你访问了一个别有用心或病毒网站,这个网站可以在网页源代码中插入js代码,使用js代码给其他服务器发送请求(比如银行的转账请求)。那么因为在发送请求的时候,浏览器会自动的把cookie发送给对应的服务器,这时候相应的服务器(比如ICBC网站),就不知道这个请求是伪造的,就被欺骗过去了。从而达到在用户不知情的情况下,给某个服务器发送了一个请求(比如转账)。

3.特点

用服务器存储Session的空间花费,换取认证的时间的缩短(用空间换时间)

二、基于Redis+Token的认证机制

1.认证流程

1.客户端登录,输入用户名和密码,后台进行验证,如果验证失败则返回登录失败的提示。如果验证成功,则服务器端生成 token 然后将 username和 token 双向绑定 (可以根据 username取出 token 也可以根据 token 取出username)存入redis,同时使用 token+userId作为key把当前时间戳也存入redis。并且给它们都设置过期时间。

在这里插入图片描述

当用户登录成功后,服务器端会回传一个Token给前端,当用户想通过前端访问比如订单服务或支付服务的时候

1.前端会带着这个Token到服务器中,服务器拿到Token后先去Redis中查询出Token对应的username

2.检查Token是否过期

2.查询出来的username就可以用来作为查询条件去数据库中查询到我们想要的信息了

在这里插入图片描述

3.Redis+Token优点

其核心优点实服务端可以主动让token失效,并且解决了Cookie+Session暴露的一些问题

4.暴露问题

Token+Redis是中心化的,要能识别token必须能访问该Redis,要求每次token都实时检测;

占用redis存储空间;

每次都要查询完Redis返回的username后,还要去数据库中查询想要的信息,增大了服务器的压力

三、基于JWT(JSON WEB TOKEN)的认证机制

1.认证流程

1.认证流程

1.前端通过Web表单将用户名和密码发送到后端接口,这一般是一个POST请求

2.后端校验通过以后,将用户不敏感的信息(id,email,role等)作为JWT的PayLoad,将其和Header分别进行Base64编码拼接后,进行Signature,形成一个JWT(Token)。形成的JWT如同一个aaa.bbb.ccc的字符串

3.后端把JWT字符串作为登录成功的结果返回给前端,前端可以将返回的结果保存在localStorage(本地缓存)中,退出登录时前端删除掉JWT即可。

4.前端可以在每次请求的时候把JWT放到HTTP Header的Authorization中(解决XSS/XSRF攻击问题)

5.后端检查前端发送的JWT是否存在(检查签名是否正确、Token是否过期等)

6.验证成功后, 再取payload出里面的信息(如username),再使用该信息直接查询用户信息完成需要的后续业务逻辑。
在这里插入图片描述

2.JWT的优点

JWT是

2.1简洁

可以通过HTTP header发送,数据量很小,传输速度快

2.2自包含

PayLoad中包含了用户的部分信息,避免多次查询数据库

2.3跨语言

因为是用JSON加密来保存的,所以原则上支持任何web形式

2.4不需要在服务端保存会话信息

也就是说使用JWT验证,由于服务端不保存用户信息,不用做sessonid复制,就解决了服务器开销过大和负载均衡问题。同时用户发请求给服务端时,前端使用JS将JWT放在header中手动发送给服务端,服务端验证header中的JWT字段,而非cookie信息,这样就避免了CSRF漏洞攻击。

2.5单点登录

一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。单点登录是当今广泛使用JWT的一项功能,因为它的开销很小并且可以在不同的域中轻松使用。

3.暴露问题

JWT是去中心化的,不是实时验证,所以本质上来说token的主动过期是做不到的(要做到就会违背初衷),一旦下发,服务后台无法拒绝携带该jwt的请求(如踢除用户);

四、JWT详解

1.JWT结构

JWT的组成(标头(Header)+有效载荷(Payload)+签名(Signature))—>header.payload.signature

1.1标头(Header)

标头通常由两部分组成︰令牌的类型(即 JWT )和所使用的签名算法,例如 HNAC SHA256 或 RSA 。它会使用 Base64 编码组成 JWT 结构的第一部分。

注意: Base64 是一种编码,也就是说,它是可以被翻译回原来的样子来的。它并不是一种加密过程。

{
    "alg" : "HS256",
    "typ" : "JWT"
}
1.2有效载荷(Payload)

令牌的第二部分是有效负载,其中包含声明。声明是有关实体(通常是用户信息)和其他数据的声明。同样的,它会使用 Base64 编码组成 JWT 结构的第二部分。

注意:不要在 Payload 中存放用户的敏感信息(比如:密码),因为令牌的第一部分和第二部分都是用 Base64 编码的

{
    "sub" : "1234567890",
    "name" : "John Doe",
    "admin" : true
}
1.3签名(Signature)

前面两部分都是使用 Base64 进行编码的,即前端可以解开知道里面的信息。Signature 需要使用编码后的 header 和 payload 以及我们提供的一个密钥,然后使用 header 中指定的签名算法(HS256)进行签名。签名的作用是保证 JWT 没有被篡改过

如:Signature=HIMACSHA256(base64UrlEncode(header) + “.” + base64Ur1Encode(payload) +secret(就是自己的一个盐));

签名目的:

最后一步签名的过程,实际上是对头部以及负载内容进行签名,防止内容被窜改。

如果有人对头部以及负载的内容解码之后进行修改,再进行编码,最后加上之前的签名组合形成新的JWT的话,那么服务器端会判断出新的头部和负载形成的签名和JWT附带上的签名是不一样的。

如果要对新的头部和负载进行签名,在不知道服务器加密时用的密钥的话,得出来的签名也是不一样的。

在这里大家一定会问一个问题:

既然我不能修改JWT,那我只直接截取到JWT发送给服务器不也一样能够通过验证吗就像是拿到Sessionid一样?

是的,首先这不是JWT的问题,而是http通讯的安全问题,总所周知http是采用的明文通讯,所以很容易就能够被窃取到http通讯报文。现在网站大多是http通讯,那也都面临着cookie被截取的问题,JWT同理。首先可以从通信层加密,比如采用https,当然https也不是绝对安全的,所以从代码层面也可以做安全检测,比如ip地址发生变化,MAC地址发生变化等等,可以要求重新登录。

2.特点

用服务器解密JWT的花费时间,换取认证的空间的减小(用时间换空间)

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐