Day76 补JWT
JWT
1.什么是JWT
Json web token (JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准,该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密
2.起源
说起JWT,我们应该来谈一谈基于token的认证和传统的session认证的区别
2.1 传统的session认证
我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证
但是这种基于session的认证使应用本身很难得到扩展,随着不同客户端用户的增加,独立的服务器已无法承载更多的用户,而这时候基于session认证应用的问题就会暴露出来
2.2 基于session认证所显露的问题
Session:每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大
扩展性:用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力
CSRF:因为是基于cookie来进行用户识别的,cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击
2.3 基于token的鉴权机制
基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利
流程上是这样的:
1.用户使用用户名密码来请求服务器
2.客户端存储token,并在每次请求时附送上这个token值
3.服务端验证token值,并返回数据
这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了Access-Control-Allow-Origin: *
3.JWT的构成
第一部分我们称它为头部(header),第二部分我们称其为载荷(payload,类似于飞机上承载的物品),第三部分是签证(signature)
header
jwt的头部承载两部分信息:
声明类型,这里是jwt声明加密的算法 通常直接使用 HMAC SHA256
完整的头部就像下面这样的JSON: { ‘typ’: ‘JWT’, ‘alg’: ‘HS256’ },然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分
playload
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
标准中注册的声明
公共的声明
私有的声明
标准中注册的声明 (建议但不强制使用) :
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻
signature:jwt的第三部分是一个签证信息
4.场景
Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用
Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JWT无疑是一种很好的方式。因为JWT可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改
5.JWT怎么用
在登录时,服务器端获取用户的信息等,根据需求生成一个JWT返回给客户端。客户端每次请求时,带上该JWT,服务器端只需要对该JWT进行解密,就可以得知该JWT是否有效、用户的信息等。这个过程服务端不需要存储JWT的内容(不同于之前类似Session id的那种token),只需要对JWT进行加解密操作
ps:之前我使用的类似Session id的token是指用随机生成的字符串作为token(同时作为缓存的key),将用户信息json化(或其他序列化方式)缓存。token返回给客户端,客户端每次请求时带上token,服务器就可以从缓存中读出用户信息,减少了数据库的压力
6.如何应用
一般是在请求头里加入Authorization,并加上Bearer标注:
fetch('api/user/1', {
headers: {
'Authorization': 'Bearer ' + token
}
})
单点登录
单点登录英文全称Single Sign On,简称SSO。它的解释是:在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。例如,网页登录了淘宝账号,天猫,钉钉等阿里系应用都不用再二次登录了。 SSO核心意义就一句话:一处登录,处处登录;一处注销,处处注销。就是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统,即用户只需要记住一组用户名和密码就可以登录所有有权限的系统
1.技术实现
1.1 基于Cookie的单点登录
这是最简单的单点登录实现方式,是使用cookie作为媒介,存放用户凭证。 用户登录父应用之后,应用返回一个加密的cookie,当用户访问子应用的时候,携带上这个cookie,授权应用解密cookie并进行校验,校验通过则登录当前用户
不难发现以上方式把信任存储在客户端的Cookie中,这种方式很容易令人质疑:
Cookie不安全
不能跨域实现免登
对于第一个问题,通过加密Cookie可以保证安全性,当然这是在源代码不泄露的前提下。如果Cookie
的加密算法泄露,攻击者通过伪造Cookie则可以伪造特定用户身份,这是很危险的
对于第二个问题,不能跨域实现免登更是硬伤。因此,有了基于Session的单点登录
1.2 基于Session的单点登录
Session解决了Cookie不能跨域的问题,但也存在其他问题。早期的单体应用使用Session实现单点登录,但现在大部分情况下都需要集群,由于存在多台服务器,Session在多台服务器之间是不共享的,因此,还需解决Session共享的问题
解决系统之间的 Session 不共享问题有以下几种方案:
Tomcat集群Session全局复制(集群内每个tomcat的session完全同步)【会影响集群的性能呢,不建议】
根据请求的IP进行Hash映射到对应的机器上(这就相当于请求的IP一直会访问同一个服务器)【如果服务器宕机了,会丢失了一大部分Session的数据,不建议】分布式Session,即把Session数据放在Redis中(使用Redis模拟Session)【建议】
2.常见方案
2.1 OAuth2(第三方登录授权)
OAuth(Open Authorization,开放授权)是为用户资源的授权定义了一个安全、开放及简单的标准,第三方无需知道用户的账号及密码,就可获取到用户的授权信息
主要应用于第三方应用授权登录:在APP或者网页接入一些第三方应用时,时常会需要用户登录另一个合作平台,比如QQ,微博,微信的授权登录,第三方应用通过oauth2方式获取用户信息
详细步骤如下(以微信登录为例):
用户访问第三方网站,第三方应用需要用户登录验证,用户选择微信授权登录
第三方应用发起微信登录授权请求
微信服务器拉起用户授权确认页面
用户授权通过
微信发送请求到第三方应用redirctUrl(第2步填写redirct_uri参数),返回凭证code与state(第2步自定义)
第三方应用获取到code之后,根据code获取accessToken
根据accessToken获取用户信息
对用户信息进行处理(用户是否第一次登录,保存用户信息,自定义token,session处理等)
返回结果(步骤1对应url或者重定向到首页)
2.2 JWT(客户端token)
难度较大,需要了解很多协议
Json web token (JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密
基于JWT认证协议,自己开发SSO服务和权限控制
以上为常见的单点登录解决方案,当然,在使用的同时也会和其他的权限授权认证的安全框架整合实现,常见的安全框架有
Spring Security
Shiro