JWT方案
什么是JWT?
JSON Web Token(JSON Web令牌)
是一个开放标准(rfc7519),它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象安全地传输信息。此信息可以验证和信任,因为它是数字签名的。jwt可以使用秘密〈使用HNAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。
通过JSON形式作为Web应用中的令牌,用于在各方之间安全地将信息作为JSON对象传输。在数据传输过程中还可以完成数据加密、签名等相关处理。
- jwt作用
- 授权:一旦用户登陆,每个后续请求将包括JWT(服务器给的)从而允许用户方法该令牌允许的路由,服务和资源。它的开销很小并且可以在不同的域中使用(后台利用密钥进行对用户信息进行数字签名,所以前端利用这个携带jwt到后端以后,后端利用密钥进行验证,就可以知道是否登陆过)。
1. 传统认证方式
1.1 认证方式
http协议本身是一种无状态的协议,如果用户向服务器提供了用户名和密码来进行用户认证,下次请求时,用户还要再一次进行用户认证才行。因为根据http协议,服务器并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储─份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样应用就能识别请求来自哪个用户。
1.2 暴露的问题
- 用户经过应用认证后,应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大;
- 用户认证后,服务端做认证记录,如果认证的记录被保存在内存中的话,用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源。在分布式的应用上,限制了负载均衡器的能力。以此限制了应用的扩展能力;
- session是基于cookie来进行用户识别,cookie如果被截获,用户很容易受到CSRF(跨站伪造请求攻击)攻击;
2.JWT认证
2.1 认证流程
- 前端通过Web表单将自己的用户名和密码发送到后端接口。该过程一般是HTTP的PSOT请求。建议的方式是通过SSL加密的传输(https),从而避免敏感信息被嗅探。
- 后端核对用户名和密码成功后,将用户的id等其他信息作为JWT的负载部分,将其与头部分别进行Base64编码拼接后,形成一个JWT(TOKEN)
- 后端将JWT字符串作为登陆成功的返回结果返回给前端。前端可以将返回的结果保存在浏览器本地缓存或session上,退出登陆时前端删除保存的JWT即可
- 前端在每次请求时将JWT放入HTTP的Header中Authorization位
- 后端检查是否存在,如存在验证JWT的有效性。例如签名是否正确,token是否过期。
- 验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作。
优缺点
- 不需要在服务端保存会话信息,特别适用于分布式微服务,并且不占用服务器的内存
- 负载中包含了一些用户的信息,不用多次查询数据库
- json格式,任何web形式都支持
- 因为没有会话信息,服务器不能主动消除认证信息
- 消息中带有认证信息,所以在重要的业务上不建议使用
3.JWT结构
就是令牌token,是一个String字符串,由3部分组成,中间用点隔开
令牌组成:
- 标头(Header)
- 有效载荷(Payload)
- 签名(Signature) token格式:head.payload.singurater 如:xxxxx.yyyy.zzzz
- Header:有令牌的类型和所使用的签名算法,如HMAC、SHA256、RSA;使用Base64编码组成;(Base64是一种编码,不是一种加密过程,可以被翻译成原来的样子)
{
"alg" : "HS256",
"type" : "JWT"
}
- Payload :有效负载,包含声明;声明是有关实体(通常是用户)和其他数据的声明,不放用户敏感的信息,如密码。同样使用Base64编码
{
"sub" : "123",
"name" : "John Do",
"admin" : true
}
- Signature :前面两部分都使用Base64进行编码,前端可以解开知道里面的信息。Signature需要使用编码后的header和payload
加上我们提供的一个密钥,使用header中指定的签名算法(HS256)进行签名。签名的作用是保证JWT没有被篡改过
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret);
签名目的:签名的过程实际上是对头部以及负载内容进行签名,防止内容被窜改。如果有人对头部以及负载的内容解码之后进行修改,再进行编码,最后加上之前的签名组合形成新的JWT的话,那么服务器端会判断出新的头部和负载形成的签名和JWT附带上的签名是不一样的。如果要对新的头部和负载进行签名,在不知道服务器加密时用的密钥的话,得出来的签名也是不一样的。
信息安全问题:Base64是一种编码,是可逆的,适合传递一些非敏感信息;JWT中不应该在负载中加入敏感的数据。如传输用户的ID被知道也是安全的,如密码不能放在JWT中;JWT常用于设计用户认证、授权系统、web的单点登录。
utoken + redis
用户登陆以后,服务器生成唯一id,将其放在redis中,并将该id返回给前端。前端再次请求的时候携带该唯一id来确定自己已经登陆过。
- 服务器可以主动删除唯一id
- 不用担心泄露验证信息,因为认证信息不被传输
- 后台占用redis内存