基于JWT的用户认证方式
在前后端开发时为什么需要用户认证呢?原因是由于HTTP协定是不存储状态的,这意味着当我们透过账号密码验证一个使用者时,当下一个request请求时他就把刚刚的资料忘记了。于是我们的程序就不知道谁是谁了。 所以为了保证系统的安全,就需要验证用户是否处于登陆状态。
传统方式
最简单的方式是:前端登录,后端根据用户信息生成一个token,并保存这个token和对应的用户id到数据或者session当中,接着把token传给用户,存入浏览器cookie,之后浏览器在请求时都会带上这个cokie,后端根据cookie值来查询用户,验证是否过期。
但是这样做页面会出现XSS漏洞,由于cookie可以被javaScript读取,XSS漏洞会导致用户token泄漏,而作为后端识别的标识,这样做是很不安全的。尽管可以进行转义输出内容,但谁也不能保证在大型项目中不会出现这个问题。
Json Web Token(JWT)
JWT是一个开放标准,它定义了一张用于简洁,自包含用于通信双方之间以JSON对象的形式安全传递的方法。JWT可以使用HMAC算法或者RSA的公钥密钥来进行签名。它具有两个特点:
- 简洁
- 可以通过URL,POST参数或者在HTTP header放松,因为数据量小,传输速度快
- 自包含
- 负载中包含了所有用户所需要的信息,避免了多次查询数据库
JWT组成
- Header头部
- 包含了两个部分,token类型和采用加密算法
{ "alg": "HS256", "typ": "JWT" }
它会使用 Base64 编码组成 JWT 结构的第一部分,如果你使用Node.js,可以用Node.js的包base64url来得到这个字符串。
- Payload负载
- 这部分就是存放信息的地方,例如可以把用户ID等信息存放在这里,面对这部分有进行详细比较,常用的由iss(签发者),exp(过期时间),sub(面向的用户),aud(接收方),iat(签发时间)。
-
{ "iss": "lion1ou JWT", "iat": 1441593502, "exp": 1441594722, "aud": "www.example.com", "sub": "[email protected]" }
- 他会使用Base64编码组成第二部分
- Signature签名
- 前两部分使用Base64编码的,即前端可以解开知道里面的信息。Signature需要使用编码后的header和payload以及我们提供的一个密钥,然后使用header中指定的算法进行签名。签名的作用是保证JWT没有被篡改过。
- 三部分通过“.”连接在一起就是JWT了,他可能是这个样子,长度和加密的算法和私钥有关系。
-
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjU3ZmVmMTY0ZTU0YWY2NGZmYzUzZGJkNSIsInhzcmYiOiI0ZWE1YzUwOGE2NTY2ZTc2MjQwNTQzZjhmZWIwNmZkNDU3Nzc3YmUzOTU0OWM0MDE2NDM2YWZkYTY1ZDIzMzBlIiwiaWF0IjoxNDc2NDI3OTMzfQ.PA3QjeyZSUh7H0GfE0vJaKW4LjKJuC3dVLQiY4hii8s
- 你可能会想,HTTP请求总带上token,这个token传来传去占用不必要的宽带资源。如果这么想了,就可以了解下HTTP2,他对头部进行了压缩,解决了这个问题。
- 签名的目的
- 进行签名是为了防止内容被篡改。如果有人对头部以及负载的内容解码时之后进行了修改,再进行编码,最后加上之前的签名组合形成新的JWT的话,那么服务端会判断出新的头部和负载形成的签名和JWT附带上的签名是不一样的。如果对新的头部和负载进行签名,在不知道服务器加密时用的密钥的话,的出来的签名是不一样的。
- 信息暴露
- Base64只是一种编码,是可以进行逆转的,因此敏感的数据是不适合放入到token当中的,他适合传递一些非敏感信息。JWT经常用于用户认证和授权系统,甚至可以实现单点登录。
- 总结
- JWT是由三部分组成
- 加密的算法和token的类型
- 用户信息(id,过期时间)
- 签名
- 这并不会对数据进行保护,使用这种方式只是为了防止内容被篡改,还有就是减少服务器的压力(不需要在数据库的查询)。
- JWT是由三部分组成
JWT使用
- 首先,前端通过Web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。
- 后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT。形成的JWT就是一个形同lll.zzz.xxx的字符串。
- 后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可。
- 前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)
- 后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)。
- 验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果。
和Session方式存储id的差异
Session方式存储用户id的最大弊病在于Session是存储在服务器端的,所以需要占用大量服务器内存,对于较大型应用而言可能还要保存许多的状态。一般而言,大型应用还需要借助一些KV数据库和一系列缓存机制来实现Session的存储。
而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。除了用户id之外,还可以存储其他的和用户相关的信息,例如该用户是否是管理员、用户所在的分组等。虽说JWT方式让服务器有一些计算压力(例如加密、编码和解码),但是这些压力相比磁盘存储而言可能就不算什么了。具体是否采用,需要在不同场景下用数据说话。
- 单点登录
Session方式来存储用户id,一开始用户的Session只会存储在一台服务器上。对于有多个子域名的站点,每个子域名至少会对应一台不同的服务器,例如:www.taobao.com
,nv.taobao.com
,nz.taobao.com
,login.taobao.com
。所以如果要实现在login.taobao.com
登录后,在其他的子域名下依然可以取到Session,这要求我们在多台服务器上同步Session。使用JWT的方式则没有这个问题的存在,因为用户的状态已经被传送到了客户端。
总结
JWT的主要作用在于(一)可附带用户信息,后端直接通过JWT获取相关信息。(二)使用本地保存,通过HTTP Header中的Authorization位提交验证。但其实关于JWT存放到哪里一直有很多讨论,有人说存放到本地存储,有人说存 cookie。
原文地址:https://www.cnblogs.com/qybk/p/10965069.html