🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 简介 前后端分离通过 Restful API 进行数据交互时,如何验证用户的登录信息及权限。 由于 HTTP 协定是不储存状态的 (stateless),这意味着当我们透过帐号密码验证一个使用者时,当下一个 request 请求时它就把刚刚的资料忘了。于是我们的程序就不知道谁是谁,就要再验证一次。 所以为了保证系统安全,我们就需要验证用户否处于登录状态。 # 传统方式 前端登录,后端根据用户信息生成一个 token,并保存这个 token 和对应的用户 id 到数据库或 Session 中,接着把 token 传给用户,存入浏览器 cookie,之后**浏览器请求带上这个 cookie**,后端根据这个 cookie 值来查询用户,验证是否过期。 ## 缺点 1. XSS 漏洞 如果我们的页面出现了 XSS 漏洞,由于 cookie 可以被 JavaScript 读取,XSS 漏洞会导致用户 token 泄露,而作为后端识别用户的标识,cookie 的泄露意味着用户信息不再安全。 尽管我们通过转义输出内容,使用 CDN 等可以尽量避免 XSS 注入,但谁也不能保证在大型的项目中不会出现这个问题。 其实你**可以设置 httpOnly 以及 secure 项**。 1. 设置 httpOnly 后 cookie 将不能被 JS 读取,浏览器会自动的把它加在请求的 header 当中; 2. 设置 secure 的话,cookie 就只允许通过 HTTPS 传输。secure 选项可以过滤掉一些使用 HTTP 协议的 XSS 注入,但并不能完全阻止。 2. 服务器端的压力 如果将验证信息保存在数据库中,后端每次都需要根据 token 查出用户 id,这就增加了数据库的查询和存储开销。若把验证信息保存在 session 中,又加大了服务器端的存储压力。 ## 疑问🤔️ httpOnly 选项使得 JS 不能读取到 cookie,那么 XSS 注入的问题也基本不用担心了。 但设置 httpOnly 就带来了另一个问题,就是很容易的被 XSRF,即跨站请求伪造。 当你浏览器开着这个页面的时候,另一个页面可以很容易的跨站请求这个页面的内容。因为 cookie 默认被发了出去。 那我们可不可以**不要服务器去查询呢**? 如果我们生成 token 遵循一定的规律,比如我们使用对称加密算法来加密用户 id 形成 token,那么服务端以后其实只要解密该 token 就可以知道用户的 id 是什么了。不过呢,我只是举个例子而已,要是真这么做,只要你的对称加密算法泄露了,其他人可以通过这种加密方式进行伪造 token,那么所有用户信息都不再安全了。恩,那用非对称加密算法来做呢,其实现在有个规范就是这样做的,接下来要介绍的 JWT。 # JWT 介绍 JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。 它定义了一种简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。 JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名。 它具备两个特点: * 简洁(Compact) 可以通过 URL,POST 参数或者在 HTTP header 发送,因为数据量小,传输速度快 * 自包含(Self-contained) 负载中包含了所有用户所需要的信息,避免了多次查询数据库 # JWT 组成 JWT 的三个部分依次如下: * Header(头部) * 进行 base64 编码 * Payload(负载) * 进行 base64 编码 * 因为 `base64` 是对称解密的,意味着该部分信息可以归类为明文信息。 * Signature(签名) * 将 Header 和 Payload 通过密钥 secret 和加盐算法进行加密后生成 写成一行,就是下面的样子: ``` Header.Payload.Signature // 示例 Token eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJVc2VySWQiOjEyMywiVXNlck5hbWUiOiJhZG1pbiJ9.Qjw1epD5P6p4Yy2yju3-fkq28PddznqRj3ESfALQy_U ``` 如果你还想确认这些信息是否真的存在,可以拷贝 JWT 串到 http://jwt.io 的在线校验工具校验一下即可。 其实到这一步可能就有人会想了,HTTP 请求总会带上 token,这样这个 token 传来传去占用不必要的带宽啊。 其实相比 cookie ,token是可选择的携带在 header 头上; 还有可以去了解下 HTTP2,HTTP2 对头部进行了压缩,相信也解决了这个问题。 ![](https://www.wangbase.com/blogimg/asset/201807/bg2018072303.jpg) # JWT 使用 ![](https://img.kancloud.cn/b1/60/b160a315a435dbdc02916b6d8622541c_919x620.png) 1. 首先,前端通过 Web 表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个 HTTP POST 请求。建议的方式是通过 SSL 加密的传输(https 协议),从而避免敏感信息被嗅探。 2. 后端核对用户名和密码成功后,将用户的 id 等其他信息作为 JWT Payload(负载),将其与头部分别进行 Base64 编码拼接后签名,形成一个 JWT。形成的 JWT 就是一个形同 `hhh.ppp.sss` 的字符串。 3. 后端将 JWT 字符串作为登录成功的返回结果返回给前端。**前端可以将返回的结果保存在 `localStorage` 或 `sessionStorage` 上,退出登录时前端删除保存的 JWT 即可**。 4. 前端在每次请求时将 JWT 放入 HTTP Header 中的 `Authorization` 位。(解决 XSS 和 XSRF 问题) ``` fetch('api/user/1', { headers: { 'Authorization': 'Bearer ' + token // JWT 规定的的表示形式 } }) ``` 5. 后端检查是否存在,如存在验证 JWT 的有效性。例如,检查签名是否正确;检查 Token 是否过期;检查 Token 的接收方是否是自己(可选)。 服务器将用户信息和自己的密钥通过既定好的算法进行签名,然后将发来的签名和生成的签名比较,严格相等则说明用户信息没被篡改和伪造,验证通过。 6. 验证通过后后端使用 JWT 中包含的用户信息进行其他逻辑操作,返回相应结果。 ## 和 Session 方式存储 id 的差异 Session 方式存储用户 id 的最大弊病在于 Session 是存储在服务器端的,所以需要占用大量服务器内存,对于较大型应用而言可能还要保存许多的状态。一般而言,大型应用还需要借助一些 KV 数据库和一系列缓存机制来实现 Session 的存储。 1. JWT 方式将用户状态分散到了客户端中,可以明显减轻服务端的 内存压力。 2. JWT 方式让服务器有一些计算压力(例如加密、编码和解码),但是这些压力相比磁盘存储而言可能就不算什么了。 除了用户 id 之外,还可以存储其他的和用户相关的信息,例如该用户是否是管理员、用户所在的分组等。 具体是否采用,需要在不同场景下用数据说话。 ## 单点登录(SSO) Session 方式来存储用户 id,一开始用户的 Session 只会存储在一台服务器上。 对于有多个子域名的站点,每个子域名至少会对应一台不同的服务器,例如:`www.taobao.com`,`nv.taobao.com`,`nz.taobao.com`,`login.taobao.com`。 所以如果要实现在`login.taobao.com`登录后,在其他的子域名下依然可以取到 Session,这要求我们在多台服务器上同步 Session。使用 JWT 的方式则不会存在这个问题,因为用户的状态已经被传送到了客户端。 # 总结 ## 优点 因为 json 的通用性,所以 JWT 是可以进行跨语言支持的,像 JAVA、JavaScript、NodeJS、PHP 等很多语言都可以使用。 因为有了 payload 部分,所以 JWT 可以在自身存储一些其他业务逻辑所必须的非敏感信息。 便于传输,jwt 的构成非常简单,字节占用很小,所以它是非常便于传输的。 它不需要在服务端保存会话信息,所以它易于应用的扩展。 ## 安全相关 1. 不应该在 jwt 的 payload 部分存放敏感信息,因为该部分是客户端可解密的部分。 2. 保护好 secret 私钥,该私钥非常重要。 3. 如果可以,请使用 https 协议。 # 其他资料 [基于 JWT 的 Token 认证的安全问题](https://www.cnblogs.com/ypppt/p/13332007.html) [基于 Token 的身份验证](https://ninghao.net/blog/2834) [Vue刷新token,判断token是否过期、失效的最简便的方法](https://blog.csdn.net/weixin_40667613/article/details/90639614) # 参考 [通过一个案例理解 JWT](https://juejin.im/post/5ba37c50e51d450e664b3fc3) [前后端分离之 JWT 用户认证](https://lion1ou.win/2017/01/18/) [前后端分离, 前端如何防止直接输入 URL 进入页面?](https://www.zhihu.com/question/269101275) [JSON Web Token 入门教程](https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html) [前后端分离模式下,如何跟踪用户状态?](https://blog.csdn.net/hwhsong/article/details/82020526)