web常见登录实现方式总结

web常见登录实现方式总结

为什么需要登录

HTTP是一种无状态的协议,客户端每次发送请求时会与服务器建立一个连接,请求之后断开连接。因此每次请求都是独立的,服务器无法判断每次请求是否是来自同一个用户。所以登录是为了让服务器在多次请求之间能识别出用户。

实现登录的思路

暂时抛开具体的技术不谈,我们先思考一下登录的实现。

以用户名密码登录为例,用户输入的用户名、密码校验成功后,登录成功。那么如何知道后续的请求都来自这个用户呢?考虑到HTTP的无状态性,后面的每次请求都应该带上一个能唯一识别用户的标识。

如果这个标识是单纯的用户名+密码,我们有两种实现思路:一种是每次都让用户输入用户名密码,这样虽然安全,但显然会造成极差的用户体验,因此舍掉。另一种是将用户名密码存储在某个位置,每次请求时自动带上,这样虽然方便,但直接存储用户名密码显然很容易被盗取,毫无安全性可言。

因此,我们想到用另一个标识代替用户名密码。这个标识最好是谁都看不懂的那种,而且它必须不重复,每个人拥有的标识都是独一无二的。由此一来,又引发出三个问题:

这个标识从哪来?客户端怎么将标识传给服务端?服务端该如何验证这个标识?

为了解决这三个问题,引入如下技术,这也是目前业界通用的登录方案:

Cookie + Session

根据该技术的实现方案,依次回答上面三个问题:

这个标识从哪来?

用户首次登录成功后,服务端会生成一个sessionId,这个sessionId就是唯一标识。这里的sessionId由我们指定的算法生成。因此这个标识由服务端生成,然后通过设置头Set-Cookie在客户端存储。

客户端怎么将标识传给服务端?

客户端从服务端拿到cookie,在之后的每次请求都带上这个cookie,cookie中存储的就是这个标识。

服务端该如何验证这个标识?

服务端在生成sessionId的同时也存储了sessionId与用户信息的映射关系。因此在验证标识的时候,会从映射关系表中查找这个sessionId,如果找到了就验证成功,并拿到用户信息,否则验证失败。

通过以上回答,你可能又会冒出一个问题:

sessionId与用户信息的映射关系在服务端是如何存储的呢?

如果单纯地设置一个对象来存储,每次进入这个服务,该对象都会初始化一次,如此一来就只能用于单用户服务。而服务端是面向多个用户的,因此需要一个额外的存储空间来存储这个映射关系。这个存储空间可以是内存、文件、数据库。比如,使用redis,key存储sessionId,value存储用户信息(JSON字符串)。

整体实现流程

缺点

由于验证信息存储在cookie中,因此无法避免CSRF

由以上的分析也可以知道,我们必须开辟一个空间来存放sessionId,而当客户端的数量非常非常大时,无疑会给服务器造成很大的压力

当服务器有很多(集群)时,每台服务器都要同步session信息,增加了服务器的维护成本

Token

为了解决上面两个问题就引入了token。既然解决了上面两个问题,就可以知道token机制与cookie无关,token不需要额外的存储空间。那么具体是怎么实现的呢?同样先回答上面的三个问题:

这个标识从哪来?

首先要知道的是,在这里,这个标识被叫做token。token同样是在用户登录校验成功后由服务端生成,业界通用的token生成方式叫做jwt。jwt的介绍之后再说。服务端生成token后通过http返回给客户端,然后由客户端自由保存。

客户端怎么将标识传给服务端?

上面说到,token机制不需要用到cookie。那么token肯定是用其他方式传到服务端的,我们通常会在请求时添加一个头部Authorization,这个字段中存储的内容就是token。Authorization这个名字时jwt规定的,如果token的生成不是用的jwt,那就可以根据其他情况再进行改变。

服务端该如何验证这个标识?

我们知道,token是通过某种算法加密生成的,token中其实存储的有用户信息,既然存在加密,当然就有解密。服务端就是通过解密算法来验证token的。具体到jwt是jwt.verify()方法。

从上面的分析可以知道,使用token其实是一种用时间换空间的策略。这里的时间就是解密算法的运行时间。而使用token与使用cookie+session的区别主要就在于标识传递方式和服务端验证方式不同。

整体实现流程

关于jwt

jwt 算法主要分为 3 个部分:header(头信息),playload(消息体),signature(签名)。

header 头部

{

"alg": "HS256", // 表示使用 HMAC-SHA256 来生成签名

"typ": "JWT"

}

payload 负载

这部分存储你想要在token里包含的用户信息,就以username为例:

{

"username": "BigDgreen",

"iat": 1594372710027 // token生成的时间

}

signature 签名

signature 部分为对前两部分的签名,这个实现需要提供一个私钥SECRECT_KEY,这个私钥只要服务器知道。签名会按照header中写的算法来生成,生成过程如下:

signature = HMACSHA256(`${base64UrlEncode(header)}.${base64UrlEncode(payload)}`,SECRECT_KEY)

最后,token的计算如下:

token = `${base64UrlEncode(header)}.${base64UrlEncode(payload)}`.signature)

缺点

token下发之后,只要在生效时间之内,就一直有效,因此无法在使用过程中途废止或者更改。

token本身包含了用户信息,被盗用后容易泄露信息。因此token的有效期应该设置的较短。而对于一些重要的权限,应再次验证。

相关推荐