这里我会讲解 web 前端常用的用户登录和权限认证的方法 —— JWT,有错误希望指正
1、Cookie + Session
???????在讲解 JWT 之前,我先介绍一下最简单的用户登录和权限认证方式:Cookie + Session
① 概述
???????HTTP 是一种无状态的协议,客户端每次发送请求时,首先要和服务器端建立一个连接,在请求完成后又会断开这个连接。这种方式可以节省传输时占用的连接资源,但同时也存在一个问题:每次请求都是独立的,服务器端无法判断本次请求和上一次请求是否来自同一个用户,进而也就无法判断用户的登录状态。
注意:上面提到 HTTP,是无状态的,但是 HTTP2 也并不能说是有状态的。 因为 HTTP 是无状态的,所以需要 cookie 来记录一些状态。HTTP2 只是对于复用单一 TCP 连接,但是从服务器角度而言依然需要 cookie 来记录一些状态,因此并不能说 HTTP2 是有状态的。
② 流程图
③ 校验步骤
- 第一步,用户登录账号,客户端发送 POST 请求,将用户名和密码发送到服务器。
- 第二步,服务器会保存用户对应的 session 数据,并且保存在自己的数据库中(当然也可以保存在客户端的内存中)。
- 第三步,服务器返回给客户端 HTTP 200 状态码,这个时候在请求头 header 中,保存了一个 Set-
Cookie: sessionid 的数据,给用户登录的 cookie设置了一个 session 的 id 值。 - 第四步,当客户端要获取用户信息的时候,发送 GET 请求,这个时候会自动携带上 cookie 信息,里面保存的就是 session 的 id 值。
- 第五步,当服务器接收到请求的时候,会查看客户端发送的请求中的 cookie 字段,并且查看服务器中是否存在该 session 的数据。
- 第六步,当服务器交验完毕后,会产生两种情况:第一种情况就是校验成功,返回给客户端 HTTP 200 状态码和客户端所需要的数据;第二种情况就是校验失败,这个时候会返回给客户端 HTTP 401 状态码,Not authorized。
???????可以看出基于 cookie 的身份验证是有状态的,意味着验证记录和会话必须同时保存在服务器端和客户端,服务器端需要跟踪记录 session,并且存在数据库中。同时客户端在 cookie 中要保存对应的 sessionid,作为 session 的唯一标识符(就像身份证一样)。
④ 存在的问题
???????这种模式的问题就是其扩展性不好,比如只有一台服务器(单机),这么做是肯定没有问题的;如果是一个服务器集群(多台服务器),就需要 session 数据共享,但是现在 session 只存在一台服务器中,所以需要每一台服务器都都读取到 session。有两种解决方案:
- 第一种解决方案是对 session 数据做一个持久化,将要共享的数据放入数据持久层当中,当各种服务器收到请求之后,都向持久层去发送数据,这种方法的优点就是架构清晰;缺点是工程量比较大,而且万一这个持久层中的数据挂了,就会单点失败。
- 第二种解决方案是使用基于 token(token base)的解决方案。这种解决方案不同于之前在服务器中要保存信息的特点,而是将信息保存在客户端,之后每次请求都将生成的信息发回到服务器。JWT(Json Web Token) 就是这种解决方案的一个代表。
2、JWT
① 流程图
② 校验步骤
- 第一步,用户登录账号,客户端发送 POST 请求,将用户名和密码发送到服务器。
- 第二步,服务器会验证用户的登录信息(用户名、密码),校验成功的时候会使用 JWT 算法,生成一个 token (已签名的 token)。
- 第三步,服务器返回给客户端 HTTP 200 状态码,并且会将生成的 token 放在请求头中(header),并不能放在请求体(body)中。客户端一般会将接收到的 token 存储在浏览器的 localstorage 或者 sessionstorage 。
- 第四步,当客户端之后再向服务器发送请求的时候,都会携带上 token (当然也可以将 token 放在 cookie 中自动发送,但是这样不能跨域发送)。最好的做法是将 token 放在 http 的头信息中的 Authorization 字段中(这是官方文档建议的做法)。
- 第五步,当服务器接收到请求的时候,接收到了 token 信息,这时候 JWT 进行反向验证,验证对应的 token 是否正确。
- 第六步,当服务器交验完毕后,会产生两种情况:第一种情况就是校验成功,返回给客户端 HTTP 200 状态码和客户端所需要的数据;第二种情况就是校验失败,这个时候会返回给客户端 HTTP 401 状态码,Not authorized。
补充: 如果客户端中的用户退出登录的时候,token 就会在客户端进行销毁(相当于直接把 localstorage 数据清除),这一步与服务器无关。
???????上面可以看出,基于 token 的身份验证是没有状态的,也就是服务器不需要记录哪些用户已经登录,或者哪些 JWT 已经被处理。之后前后端每一次通信都会携带上这个 token。服务器通过 token 就可以认定用户的身份,服务器也就变成无状态的了。这种权限校验模式非常容易实现扩展,这个时候部署多个服务器都是可以的了。
③ 解读 token 中存储的内容
-
这里根据我自己做的项目中的一个 swagger 接口(进行用户登录的接口),来讲解一下 token 中存储的数据。调用该接口后,复制其中 token 内的信息。 -
然后打开 JWT 官网:https://jwt.io/#debugger-io,将复制的信息粘贴至 Encoded 中,然后右边会通过解码将 token 解析出来。 2.1 讲解一下右侧 token 解析出来的内容: Ⅰ. HEADER :这是一个 JSON 对象,描述 JWT 的元数据。这里面也存储着两个信息:ALGORITHM 和 TOKEN TYPE
ALGORITHM :即 JSON 对象中的 alg,表示 JWT 通过哪种算法进行加密,下列是 JWT 常用的加密算法 TOKEN TYPE :即 JSON 对象中的 typ,指定该数据是什么类型,就是 JWT 类型
Ⅱ. PAYLOAD :这也是一个JSON 对象,用来存放实际需要传递的数据,data 中就是用户的各种信息。在最后两行多了两个字段:exp 和 iat
exp :这个存储的数字是 token 的过期时间(有效期的截止日期) iat :这个存储的数字是 token 什么的发送时间(即 JWT 什么时候生成的)
Ⅲ. VERIFY SIGNATURE :signature 是对前两个部分的签名,防止数据篡改,这个需要一个指定的密钥 secret,这个密钥只有服务器才知道,不能泄露给用户。
???????上面这三块就是 token 的组成内容:HEADER、PAYLOAD、VERIFY SIGNATURE,token 在用户登录的时候就会将其存储在本地,下次再发送数据请求的时候,就需要将 token 加在 header 中一起发送给服务器
④ 对 Swagger 一个功能的补充
???????在 Swagger 中,提供了一个功能,可以将 token 永久的保存在 header 中,后续的请求就会自动携带上 token
- 点击 Authorize
- 输入 Bearer + token(这个需要将下面请求的 token 中复制过来)
- 这下测试一下其他的请求接口,看看 token 是否会被自动添加上,如下图,token 会被自动添加上。
3、总结
???????其实进行用户登录和权限认证的方式用很多种,这里我主要是详细的对 JWT 做了一个讲解,有兴趣的可以去了解下 SSO 单点登录和 OAuth 第三方登录。 ???????这里我再抛出一个问题,上面我提到的,将 token 存放于请求头 header 中,而不能存放在请求体 body,这一点也可以去深入的了解一下。
|