Json web token (JWT), 是为了在网络运用环境间通报声明而实行的一种基于JSON的开放标准((RFC 7519).
该token被设计为紧凑且安全的,特殊适用于分布式站点的单点登录(SSO)场景。
JWT的声明一样平常被用来在身份供应者和做事供应者间通报被认证的用户身份信息,以便于从资源做事器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

二、JWT的组成
1、JWT天生编码后的样子:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q
2、JWT由三部分构成
(1) 头部:header,jwt的头部承载两部分信息:
声明类型,这里是jwt声明加密的算法 常日直策应用 HMAC SHA256完全的头部就像下面这样的JSON:
{ 'typ': 'JWT', 'alg': 'HS256'}
然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
(2) 载荷:playload,载荷便是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分:
标准中注册的声明公共的声明私有的声明标准中注册的声明 (建议但不逼迫利用) :
iss: jwt签发者sub: jwt所面向的用户aud: 吸收jwt的一方exp: jwt的过期韶光,这个过期韶光必须要大于签发韶光nbf: 定义在什么韶光之前,该jwt都是不可用的.iat: jwt的签发韶光jti: jwt的唯一身份标识,紧张用来作为一次性token,从而回避重放攻击。公共的声明 :
公共的声明可以添加任何的信息,一样平常添加用户的干系信息或其他业务须要的必要信息.但不建议添加敏感信息,由于该部分在客户端可解密.
私有的声明 :
私有声明是供应者和消费者所共同定义的声明,一样平常不建议存放敏感信息,由于base64是对称解密的,意味着该部分信息可以归类为明文信息。
定义一个payload:
{ "sub": "1234567890", "name": "John Doe", "admin": true}
然后将其进行base64加密,得到Jwt的第二部分
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
(3) 署名:signature,jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
header (base64后的)payload (base64后的)secret这个部分须要base64加密后的header和base64加密后的payload利用.连接组成的字符串(头部在前),然后通过header中声明的加密办法进行加盐secret组合加密,然后就构成了jwt的第三部分。
UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q
密钥secret是保存在做事真个,做事端会根据这个密钥进行天生token和验证,以是须要保护好。
3、署名的目的
末了一步署名的过程,实际上是敌人部以及载荷内容进行署名。一样平常而言,加密算法对付不同的输入产生的输出总是不一样的。对付两个不同的输入,产生同样的输出的概率极其地小(有可能比我成天下首富的概率还小)。以是,我们就把“不一样的输入产生不一样的输出”当做一定事宜来看待吧。
以是,如果有人敌人部以及载荷的内容解码之后进行修正,再进行编码的话,那么新的头部和载荷的署名和之前的署名就将是不一样的。而且,如果不知道做事器加密的时候用的密钥的话,得出来的署名也一定会是不一样的。
做事器运用在接管到JWT后,会首先敌人部和载荷的内容用同一算法再次署名。那么做事器运用是怎么知道我们用的是哪一种算法呢?别忘了,我们在JWT的头部中已经用alg字段指明了我们的加密算法了。
如果做事器运用敌人部和载荷再次以同样方法署名之后创造,自己打算出来的署名和接管到的署名不一样,那么就解释这个Token的内容被别人动过的,我们该当谢绝这个Token,返回一个HTTP 401 Unauthorized相应。
把稳:在JWT中,不应该在载荷里面加入任何敏感的数据,比如用户的密码。
4、如何运用
一样平常是在要求头里加入Authorization,并加上Bearer标注:
fetch('api/user/1', { headers: { 'Authorization': 'Bearer ' + token }})
做事端会验证token,如果验证通过就会返回相应的资源。
5、安全干系
不应该在jwt的payload部分存放敏感信息,由于该部分是客户端可解密的部分。保护好secret私钥,该私钥非常主要。如果可以,请利用https协议6、对Token认证的五点认识
一个Token便是一些信息的凑集;在Token中包含足够多的信息,以便在后续要求中减少查询数据库的几率;做事端须要对cookie和HTTP Authrorization Header进行Token信息的检讨;基于上一点,你可以用一套token认证代码来面对浏览器类客户端和非浏览器类客户端;由于token是被署名的,以是我们可以认为一个可以解码认证通过的token是由我们系统发放的,个中带的信息是合法有效的;三、传统的session认证
我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的运用供应了用户名和密码来进行用户认证,那么下一次要求时,用户还要再一次进行用户认证才行,由于根据http协议,我们并不能知道是哪个用户发出的要求,所以为了让我们的运用能识别是哪个用户发出的要求,我们只能在做事器存储一份用户登录的信息,这份登录信息会在相应时通报给浏览器,见告其保存为cookie,以便下次要求时发送给我们的运用,这样我们的运用就能识别要求来自哪个用户了,这便是传统的基于session认证。
但是这种基于session的认证使运用本身很难得到扩展,随着不同客户端用户的增加,独立的做事器已无法承载更多的用户,而这时候基于session认证运用的问题就会暴露出来。
基于session认证所显露的问题
Session: 每个用户经由我们的运用认证之后,我们的运用都要在做事端做一次记录,以方便用户下次要求的鉴别,常日而言session都是保存在内存中,而随着认证用户的增多,做事真个开销会明显增大。
扩展性: 用户认证之后,做事端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次要求还必须要要求在这台做事器上,这样才能拿到授权的资源,这样在分布式的运用上,相应的限定了负载均衡器的能力。这也意味着限定了运用的扩展能力。
CSRF: 由于是基于cookie来进行用户识别的, cookie如果被截获,用户就会很随意马虎受到跨站要求假造的攻击。
基于token的鉴权机制
基于token的鉴权机制类似于http协议也是无状态的,它不须要在做事端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的运用不须要去考虑用户在哪一台做事器登录了,这就为运用的扩展供应了便利。
流程上是这样的:
用户利用用户名密码来要求做事器做事器进行验证用户的信息做事器通过验证发送给用户一个token客户端存储token,并在每次要求时附送上这个token值做事端验证token值,并返回数据这个token必须要在每次要求时通报给做事端,它该当保存在要求头里, 其余,做事端要支持CORS(跨来源资源共享)策略,一样平常我们在做事端这么做就可以了 Access-Control-Allow-Origin:。
四、token的优点
支持跨域访问: Cookie是不许可垮域访问的,这一点对Token机制是不存在的,条件是传输的用户认证信息通过HTTP头传输。无状态(也称:做事端可扩展行):Token机制在做事端不须要存储session信息,由于Token 自身包含了所有登任命户的信息,只须要在客户真个cookie或本地介质存储状态信息。更适用CDN: 可以通过内容分发网络要求你做事真个所有资料(如:javascript,HTML,图片等),而你的做事端只要供应API即可。去耦: 不须要绑定到一个特定的身份验证方案。Token可以在任何地方天生,只要在你的API被调用的时候,你可以进行Token天生调用即可。更适用于移动运用: 当你的客户端是一个原平生台(iOS, Android,Windows 8等)时,Cookie是不被支持的(你须要通过Cookie容器进行处理),这时采取Token认证机制就会大略得多。CSRF:由于不再依赖于Cookie,以是你就不须要考虑对CSRF(跨站要求假造)的戒备。性能: 一次网络来回韶光(通过数据库查询session信息)总比做一次HMACSHA256打算 的Token验证和解析要费时得多。不须要为登录页面做分外处理: 如果你利用Protractor 做功能测试的时候,不再须要为登录页面做分外处理。基于标准化:你的API可以采取标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft)。由于json的通用性,以是JWT是可以进行跨措辞支持的,像JAVA,JavaScript,NodeJS,PHP等很多措辞都可以利用。由于有了payload部分,以是JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。便于传输,jwt的构成非常大略,字节占用很小,以是它是非常便于传输的。它不须要在做事端保存会话信息, 以是它易于运用的扩展。五、JWT的JAVA实现
Java中对JWT的支持可以考虑利用JJWT开源库;JJWT实现了JWT, JWS, JWE 和 JWA RFC规范;
下面将大略举例解释其利用:
1、天生Token码
import javax.crypto.spec.SecretKeySpec;import javax.xml.bind.DatatypeConverter;import java.security.Key;import io.jsonwebtoken.;import java.util.Date; //Sample method to construct a JWT private String createJWT(String id, String issuer, String subject, long ttlMillis) { //The JWT signature algorithm we will be using to sign the tokenSignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis); //We will sign our JWT with our ApiKey secretbyte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(apiKey.getSecret());Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); //Let's set the JWT ClaimsJwtBuilder builder = Jwts.builder().setId(id) .setIssuedAt(now) .setSubject(subject) .setIssuer(issuer) .signWith(signatureAlgorithm, signingKey); //if it has been specified, let's add the expirationif (ttlMillis >= 0) { long expMillis = nowMillis + ttlMillis; Date exp = new Date(expMillis); builder.setExpiration(exp);} //Builds the JWT and serializes it to a compact, URL-safe stringreturn builder.compact();}
2、解码和验证Token码
import javax.xml.bind.DatatypeConverter;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.Claims; //Sample method to validate and read the JWTprivate void parseJWT(String jwt) {//This line will throw an exception if it is not a signed JWS (as expected)Claims claims = Jwts.parser() .setSigningKey(DatatypeConverter.parseBase64Binary(apiKey.getSecret())) .parseClaimsJws(jwt).getBody();System.out.println("ID: " + claims.getId());System.out.println("Subject: " + claims.getSubject());System.out.println("Issuer: " + claims.getIssuer());System.out.println("Expiration: " + claims.getExpiration());}