Blazor和Vue对比学习(进阶2.2.4):状态管理之持久化保存(2),Cookie/Session/jwt

2022-08-05

注:本节涉及到前后端,这个系列的对比学习,还是专注在前端VueBlazor技术,所以就不撸码了,下面主要学习概念。

 

我们知道,Http是无状态协议,客户端请求服务端,认证一次后,如果再次请求,又要重新认证,因为对服务端来说,客户端的每次请求都是无差别的!另外,服务端的授权体系,一般使用基于RBAC角色权限模型。角色信息,我们可以在客户端每次请求都,都查询一次,但这样比较消耗资源。最好的方式,是客户端第一次请求时,就将角色传给客户端,之后客户端每次请求,直接携带角色信息。而这些问题,都需要使用Cookie、Session或者jwt来解决。

 

1、先说Cookie。下图为Cookie在客户端与服务端的应用逻辑图。

 

 

如上图所示:

  • 客户端首次请求服务器时,携带参数(如用户名和密码),服务器根据参数判断是否合法。如合法,则在Response中颁发Cookies,如在.NET中,写入Cookie,【Response.Cookies[name].value = functionMC;Response.Cookies[name].Expires=DateTime.Now.AddDays(1);】,其中Expires属性,为服务端设置的过期时间。
  • 客户端收到服务器的Response后,将Cookie以健值对的方式保存到浏览器中,如使用js直接操作DOM,【document.Cookie="username=John Doe; expires=Thu, 18 Dec 2043 12:00:00 GMT";】,客户端也可以设置过期时间。如不设置,保存在内存中,浏览器关闭时自动清除;如设置,则保存在本地硬盘中,到期后自动清除。
  • 之后,客户端每次访问服务器,都会在请求头中携带相应站点的Cookie。服务端就可以读取Cookie,如在.NET中,读取Cookie,【string name = Request.Cookies[name].value
  • Cookie在安全性方面,易出现Cookie劫持和欺骗,大小和数量方面也受限制。所以在Cookie之后,出现了Session

 

2、Session。下图为Session在客户端与服务端的应用逻辑图

 

如上图所示

  • 客户端首次访问服务器后,判断是否合法,如果合法,则在服务器的缓存中建立一个键值对,键为SessionID,同时将SessionID作为Cookie返回给客户端。客户端再次请求时,请求头携带SessionID,服务端根据SessionID,查找缓存信息,根据缓存信息进行处理。
  • 使用Session后,客户端与服务端之间只传递SessionID,更多信息在服务端缓存中,这样可以保存更多信息。Session可以设置有效期,也可以不设置。如不设置,则只在当前会话中有效,客户端关闭后就失效,这样也更安全。
  • Session本身的含义,指客户端与服务端之间的会话,背后实质指的是一种服务器缓存,如果请求不是很多,效率还是很高的
  • Session可以有效利用服务端资源,不受客户端限制,安全性更好。但是,当客户端的并发请求比较多时,会很占服务器内存。如果是分布式,不同的Session存储在不同的服务器之间,而客户端每次请求的路径是随配的,要解决分布式,我们就需要在每台服务器之间同步SessionID和缓存信息。最后,SessionID还是依赖于CookieCookie的跨域、单点登陆难等问题,它也一样有。所以,token的方案开始出现,它是目前最主流的方案,而其中最重要的标准,就是jwt

 

 

3、jwt。下图为token/jwt在客户端和服务端的应用逻辑图

 

如上图所示,jwt重新将信息保存在了客户端,节省了服务端资源,也没有分布式的问题,同时在灵活性和安全性方面有了质的提升。

  • 灵活性方面:不再限于Cookie,可以保存到本地的StorageIndexedDB,甚至于远程数据库里,更加的自由。同时,它以JSON加密形式保存在客户端的,是跨语言的,原则上任何web形式都支持。
  • 安全性方面:可以说有了质的提升,这才是jwt的核心。首先它可以完全不依赖于Cookie,它没有跨域、单点登陆等问题;其次,它将加密和解密的过程,放在了服务器上,即使信息被截获,也无法篡改。
  • 更好的支持分布式:在Session方案中,我们需要在每个服务器都进行缓存同步。而jwt中,我们只要在每个服务器都拷备一份密钥即可,或者可以将密钥统一保存到一个Redis服务器中,每个服务器统一向Redis请求密钥,这样连拷贝密钥的工作也可省去

 

下面简单说明一下jwt的构成、生成、加密和解密过程:

  • jwtHeaderPlayloadSignature三部分组成,以符号“.”连接成字符串。其中Header是一个json对象的Base64编码,主要有签名的算法信息;Playload也是一个json对象的Base64编码,这是传输的主体内容,比如用户角色、所在部门等信息都可以存放在里面。我们知道Base64不具有加密能力,可以说HeaderPlayload是明文传输的,在Playload里面,千万不能放敏感信息。而Signature才是jwt安全体系的核心
  • jwt是在服务端生成的,我们定义好HeaderPlayload后,在服务端我们有一个密钥Secret,这个密钥是一个没有规律的字符串。我们使用Header里的哈希算法,加上密钥、HeaderPlayload,就可以算出签名SignatureSignature是不可匿的,无法破解,破解也没意义。
  • 客户端再次请求时,服务端获得请求头携带的jwt后,取出HeaderPlayloadSignature(A)。对Header进行Base64解码,获取其中的哈希算法,然后重新使用这个哈希算法,加上密钥、HeaderPlayload重新算出一个签名Signature(B),然后比对Signature(A)Signature(B)是否一致,如果一致,则请求是合法的,服务端放行,并使用Playload中的信息进行计算处理。
  • 服务端的密钥Secret,是计算签名的安全核心,一定要保存好,最好定期更新。