JWT 产生背景
客户端通过调用服务端的接口,访问服务端业务数据,但是,一般的数据并非所有的用户都有权限去访问。因此,为了保证数据的安全性,在访问者访问数据时需要验证此次请求是否有足够的权限去访问此数据,也就是对访问者进行鉴权。
由于HTTP协议是无状态的,也就是说,如果我们已经认证了一个用户,那么他下一次请求的时候,服务器不知道我是谁,所以这就导致我们必须再次认证。
为了解决以上问题,目前有两种方式,如下:
基于服务器的身份认证
基于Token的身份认证
1.1 传统基于服务的认证方式
传统的做法是将已经认证过的用户信息存储在服务器上,比如Session。用户下次请求的时候带着Session ID,然后服务器以此检查用户是否认证过。
基于服务的认证方式的不足
Sessions:用户通过认证之后,服务对就会为其创建一个session存放在服务端,通常会放到服务器的内存之中,这无形之中给服务器增加了负担,开销会越来越大。
扩展性:每一个用户认证同过,对应的session信息就会存在放在认证服务器的内存之中,如果在集群部署的情况下,在没有使用ip_hash的情况下,会出现一些重复登录的问题。
CORS:在移动端发展如此迅猛的情况下,往往是一套后端服务,支持多种前端设备,尤其是我们的数据被多个移动端使用的情况,这时我们就必须要考虑跨域资源共享的问题了。或者当使用ajax从另一个域名下调用服务资源时,我们可能会遇到禁止请求的问题。
CSRF:是的用户容易遭到CSRF攻击。
1.2 基于Token的身份认证
基于Token的身份认证是无状态的,所以在服务器端或session中不会存储任何与用户有关的信息。这样也很好的解决了服务的集群部署问题,即使部署多台服务器,因为每个服务都不需要存储用户信息,所以服务可以根据需求进行横向扩展。
认证流程实现
用户使用用户名和密码请求登录认证
服务器端根据用户提供的用户名和密码进行验证;
认证成功之后,服务器端生成用户唯一标识的token返回;
客户端保存服务器端返回的token,并在随后的每一次请求中,携带此token;
客户端携带token访问服务器端接口,服务器端验证token,验证成功则访问数据,验证失败则直接返回提示。
基于Token的认证方式的优点
无状态支持扩展:由于服务器端不存储用户登录的状态,有关用户登录的数据信息皆存在客户端,每次请求时携带用户登录信息。因此负载均衡器可以将用户的请求分配到任何一个后台不服务器上去处理请求数据。
避免CSRF攻击:token常常被放到 HTTP Header 中,因此并不需要携带Cookie。当然,你也可以将token放到Cookie中,但是此时的Cookie只是一个存储机制,而非基于Cookie的认证机制。
JWT就是一种有效的,基于Token的身份认证方式。
2. JWT 简介
2.1 JWT 是什么
JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
2.2 JWT 结构说明
JWT 采用一种紧凑的格式,由三个部分组成,使用.隔开,这是哪个部分分别是:
Header(头部)
Payload(负载)
Signature(签名)
因此,将上面的几个部分组成一体,就成了以下一种格式:
header.payload.signature
// e.g.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 // header
.eyJrZXkiOiJ2YWwiLCJpYXQiOjE0MjI2MDU0NDV9 // payload
.eUiabuiKv-8PYk2AkGY4Fb5KMZeorYBLw261JPQD5lM // signature
下面就这几部分分别介绍以下。
2.2.1 Header
典型的Header由两个不分组成,一个是token的类型,这里我们使用的是JWT,另外一部分就是签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256),或者是RSA,如下所示:
{
"alg": "HS256",
"typ": "JWT"
}
然后,再使用Base64Url 对上面的 JSON 对象进行整体编码,得到编码后的Header。
2.2.2 Payload
令牌的第二部分是有效负载(Payload),其中包含声明(claims)。 声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型: registered,public 和 private。
Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐。比如
iss (issuer): 签发人
exp (expiration time): 过期时间
sub (subject): 主题
aud (audience): 受众
nbf (Not Before): 生效时间
iat (Issued At): 签发时间
jti (JWT ID): 编号
Public claims : 可以随意定义。
Private claims : 用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明。
一个完成的Payload如下所示:
{
"sub": "test",
"name": "JDKONG",
"admin": true
}
JWT 默认是不加密的,任何人都可以读到,因此不要在JWT的payload或header中放置敏感信息,除非它们是加密的。
对payload进行Base64编码就得到 JWT 的第二部分。
2.2.3 Signature
Signature 部分是 对前两部分 的 签名,目的是:防止数据篡改。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。
输出是三个由"点"分隔的Base64-URL字符串,可以在HTML和HTTP环境中轻松传递,而与基于XML的标准(如SAML)相比更加紧凑。
下面显示了一个JWT,它具有先前的头和有效负载编码,并使用机密签名。
3. JWT 是如何工作的?
在身份验证中,当用户使用其凭据成功登录时,将返回JSON Web令牌。 由于令牌是凭证,因此必须非常小心以防止出现安全问题。 一般情况下,不应该将令牌保留的时间超过要求。
无论何时用户想要访问受保护的路由或者资源的时候,用户代理(通常是浏览器)都应该带上JWT,典型的,通常放在Authorization header中,用Bearer schema。
Authorization: Bearer
服务器上的受保护的路由将会检查Authorization header中的JWT是否有效,如果有效,则用户可以访问受保护的资源。如果JWT包含足够多的必需的数据,那么就可以减少对某些操作的数据库查询的需要,尽管可能并不总是如此。
如果token是在授权头(Authorization header)中发送的,那么跨源资源共享(CORS)将不会成为问题,因为它不使用cookie。
下面这张图显示了如何获取JWT以及使用它来访问APIs或者资源:
应用或者客户端请求授权服务器进行授权
授予授权后,授权服务器会向应用程序返回访问令牌。
应用程序使用访问令牌来访问受保护资源(如API)。
4. 我们为什么要使用JSON Web令牌?
让我们来谈谈JSON Web Tokens(JWT)与Simple Web Tokens(SWT)和Security Assertion Markup Language Tokens(SAML)相比的好处。
由于JSON比XML更简洁,因此在编码时它的大小也更小,使得JWT比SAML更紧凑。 这使得JWT成为在HTML和HTTP环境中传递的不错选择。
在安全方面,SWT只能使用HMAC算法通过共享密钥对称签名。 但是,JWT和SAML令牌可以使用X.509证书形式的公钥/私钥对进行签名。 与签名JSON的简单性相比,使用XML数字签名对XML进行签名而不会引入模糊的安全漏洞非常困难。
JSON解析器在大多数编程语言中很常见,因为它们直接映射到对象。 相反,XML没有自然的文档到对象映射。 这使得使用JWT比使用SAML断言更容易。
关于使用,JWT用于互联网规模。 这突出了在多个平台(尤其是移动平台)上轻松进行JSON Web令牌的客户端处理。
比较编码的JWT和编码的SAML的长度
5. 为什么有人不推荐使用 JWT?
(可参考:讲真,别再使用JWT了!- FROM ThoughtWorks中国)
5.1 为什么不推荐使用JWT?
**更多的空间占用。**如果将原存在服务端session中的各类信息都放在JWT中保存在客户端,可能造成JWT占用的空间变大,需要考虑cookie的空间限制等因素,如果放在Local Storage,则可能受到XSS攻击。
**无法作废已颁布的令牌。**所有的认证信息都在JWT中,由于在服务端没有状态,即使你知道了某个JWT被盗取了,你也没有办法将其作废。在JWT过期之前(你绝对应该设置过期时间),你无能为力。
**不易应对数据过期。**与上一条类似,JWT有点类似缓存,由于无法作废已颁布的令牌,在其过期前,你只能忍受“过期”的数据。
5.2 如何解决JWT已存在问题?
考虑到cookie的空间限制(大约4k左右),在JWT中尽可能只放“够用”的认证信息,其他信息放在数据库,需要时再获取,同时也解决之前提到的数据过期问题
在JWT的内容中加入一个随机值作为CSRF令牌,由服务端将该CSRF令牌也保存在cookie中,但设置HttpOnly=false,这样前端Javascript代码就可以取得该CSRF令牌,并在请求API时作为HTTP header传回。服务端在认证时,从JWT中取出CSRF令牌与header中获得CSRF令牌比较,从而实现对CSRF攻击的防护
6. JWT 的使用场景有哪些?
下列场景中使用JSON Web Token是很有用的:
Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。
Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWTs可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。