JWTIbelievethatmanyfriendsknowthatJSONWebToken,ifyouusejjwttosupportJWTintheproject,youmayonlyneedtounderstandtheconceptofJWT,butnowmanytimeswemaynotusejjwt,butchoosenimbus-jose-jwtlibrary,atthistimeitispossibletocomeintocontactwithsomenewconcepts,suchasJWEandJWS.SowhatistherelationshipbetweenJWE,JWSandJWT?BrotherSongrecentlysawagoodarticleaboutthis,let'stakealooktogether,thefollowingisthetext.什么是JWT一个JWT,应该是如下形式的:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ这些东西看上很凌乱,但是非常紧凑,并且是可打印的主要用于验证签名的真实性。WhatproblemdoesJWTsolve?ThemainpurposeofJWTistotransferclaimsbetweenserverandclientinasecuremanner.Themainapplicationscenariosareasfollows:AuthenticationAuthentication;AuthorizationAuthorization//Payattentiontothedifferencebetweenthesetwowords;jointidentification;clientsession(statelesssession);clientconfidentiality.SometermsofJWTexplainJWS:SignedJWTsignedjwtJWE:EncryptedJWTpartofthepayloadisencryptedjwt;currently,theoperationofencryptingpayloadisnotverypopular;JWK:JWTkey,whichiswhatweoftencallsecret;JWKset:Inasymmetricencryption,JWTkeysetneedsakeypairinsteadofaseparatekey,whichwillbeexplainedlater;JWA:thecryptographicalgorithmusedbythecurrentJWT;nonsecureJWT:whenthesignaturealgorithmoftheheaderissetWhenitissettonone,theJWTisinsecure;becausethesignaturepartisblank,everyonecanmodifyit.JWT的组成大家通常看到的一个jwt由以下三部分组成,分别是:header:主要声明了JWT的签名算法;payload:主要携带各种声明,传输明文数据;signature:拥有这部分的JWT称为JWS,即signedJWS;没有这部分的JWT称为nonsecureJWT,即不安全的JWT。此时header中声明的签名算法为none。这三部分以·划分。像xxxxx.yyyyy.zzzzz这样的样式。JWTheader{"typ":"JWT","alg":"none","jti":"4f1g23a12aa"}jwtheader的组成header通常由两部分组成:token的类型,也就是JWT,使用的令牌类型哈希算法,例如HMACSHA256或RSA。当然还有两个可选的部分,一个是jti,也就是JWTID,代表正在使用的JWT的编号,这个编号在对应的服务器上应该是唯一的。当然jti也可以放在payload里面。另一个是cty,也就是内容类型。这是比较少见的。当payload是任意数据时,这个header不需要设置,但是当content中也包含jwt时。即在嵌套JWT时,这个值必须设置为jwt。这种情况比较少见。jwt头加密算法如下:base64UrlEncode(header)>>eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIiwianRpIjoiNGYxZzIzYTEyYWEifQJWTpayload{"iss":"http://shaobaobaoer.cn","aud":"http://shaobaobaoerwt.cn/webtest/j","jti":"4f1g23a12aa","iat":1534070547,"nbf":1534070607,"exp":1534074147,"uid":1,"data":{"uname":"shaobao","uEmail":"shaobaobaoer@126.com","uID":"0xA0","uGroup":"guest"}}jwtpayload的组成通常由三部分组成,分别是RegisteredClaims;公开索赔;私人索赔;每个语句都有自己的字段。RegisteredClaimsiss[issuer]issuersub[subject]用户的url地址JWT是针对的,用于处理特定应用,不是常用字段aud[audience]接收者的url地址exp[expiration]thejwt被销毁的时间;unixtimestampnbf[notbefore]jwt不能早于这个时间使用;unixtimestampiat[issuedat]jwt的发布时间;unixtimestampjti[JWTID]jwtPublicClaims的唯一ID号这些可以由使用JWT的标准化组织根据需要定义,可以参考文档IANAJSONWebTokenRegistry。私人声明这些是为在同意使用它们的各方之间共享信息而创建的自定义声明,既不是注册声明也不是公共声明。在上面的有效负载中,没有公共声明,只有私有声明。jwtpayload的加密算法加密的方式如下:base64UrlEncode(payload)>>eyJpc3MiOiJodHRwOi8vc2hhb2Jhb2Jhb2VyLmNuIiwiYXVkIjoiaHR0cDovL3NoYW9iYW9iYW9lci5jbi93ZWJ0ZXN0L2p3dF9hdXRoLyIsImp0aSI6IjRmMWcyM2ExMmFhIiwiaWF0IjoxNTM0MDcwNTQ3LCJuYmYiOjE1MzQwNzA2MDcsImV4cCI6MTUzNDA3NDE0NywidWlkIjoxLCJkYXRhIjp7InVuYW1lIjoic2hhb2JhbyIsInVFbWFpbCI6InNoYW9iYW9iYW9lckAxMjYuY29tIiwidUlEIjoiMHhBMCIsInVHcm91cCI6Imd1ZXN0In19暴露的信息所以,在JWT中,不应该在载荷里面加入任何敏感的数据。Intheaboveexample,wearetransmittingtheuser'sUserID,email地址等。这个值其实并不敏感,知道一般是安全的。但是密码等内容不能放在JWT中。如果你把用户的密码放在JWT中,那么恶意的第三方就可以通过Base64解码快速知道你的密码。当然,有一个解决方案,那就是对负载进行加密。后面会讲到JWS的概念。JWS的结构JWS,即JWTSignature,它的结构是在之前的nonsecureJWT的基础上,在header中声明签名算法,在最后加上signature。创建签名是为了保证jwt不能被他人篡改。为了完成签名,除了头部信息和载荷信息外,还需要算法的密钥,即秘密。当使用非对称加密方法时,这里的秘密是私钥。为了方便下文的展开,我们将JWT密钥或密钥对统称为JSONWebKey,即JWK。jwt签名的签名算法RSASSA||ECDSA||HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)>>GQPGEpixjPZSZ7CmqXB-KIGNzNl4Y86d3XOaRsfiXmQ>>#以上是HMACSHA256生成到此为止,jwt有三种类型的签名算法。对称加密HMAC【哈希消息认证码】:HS256/HS384/HS512非对称加密RSASSA【RSA签名算法】(RS256/RS384/RS512)ECDSA【椭圆曲线数据签名算法】(ES256/ES384/ES512)最后签名Connect与前两段同。得到一个签名的JWT,也就是JWS。eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImp0aSI6IjRmMWcyM2ExMmFhIn0.eyJpc3MiOiJodHRwOi8vc2hhb2Jhb2Jhb2VyLmNuIiwiYXVkIjoiaHR0cDovL3NoYW9iYW9iYW9lci5jbi93ZWJ0ZXN0L2p3dF9hdXRoLyIsImp0aSI6IjRmMWcyM2ExMmFhIiwiaWF0IjoxNTM0MDcwNTQ3LCJuYmYiOjE1MzQwNzA2MDcsImV4cCI6MTUzNDA3NDE0NywidWlkIjoxLCJkYXRhIjp7InVuYW1lIjoic2hhb2JhbyIsInVFbWFpbCI6InNoYW9iYW9iYW9lckAxMjYuY29tIiwidUlEIjoiMHhBMCIsInVHcm91cCI6Imd1ZXN0In19.GQPGEpixjPZSZ7CmqXB-KIGNzNl4Y86d3XOaRsfiXmQ当验证签名的时候,利用公钥或者密钥来解密Sign,和base64UrlEncode(header)+"."+base64UrlEncode(payload)的内容完全一样的时候,表示验证通过。IfyouhavesomeconceptsaboutCAintheextraheaderdeclarationofJWS,thesecontentswillbeeasiertounderstand.Inordertoensurethattheserver'skeypairisreliableandvalid,anditisalsoconvenientforthird-partyCAagenciestosignJWTinsteadofthelocalservertosignJWT,therecanbeadditionaldeclarationsfortheJWSheader.Thefollowingdeclarationsareoptional,dependingonHowtouseJWS.Asfollows:jku:theaddresstosendtheJWK;itisbesttouseHTTPStotransmitjwk:theJWKmentionedearlierkid:theIDnumberofthejwkx5u:theURLpointingtoasetofX509publiccertificatesx5c:theX509certificatechainx5t:theX509certificateSHA-1fingerprintx5t#S256:SHA-256fingerprintofX509certificatetyp:JOSEandJOSE+JSONareaddedtotheoriginalunencryptedJWT.JOSEserializationwillbementionedlater.AppliestothecasewheretheJOSEheader'sobjectismixedwiththisJWT.crit:包含声明名称的字符串数组,用作实现定义的扩展,必须由this->JWT的解析器处理。异常。多重身份验证和JWS序列化当多重签名或JOSE标头对象与JWS混合时,通常需要JWS序列化。JWS的序列化结构如下所示:{"payload":"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ","signatures":[{"protected":"eyJhbGciOiJSUzI1NiJ9","header":{"kid":"2010-12-29"},“签名”:“signature1”},{“protected”:“eyJhbGciOiJSUzI1NiJ9”,“header”:{“kid”:“e9bc097a-ce51-4036-9562-d2ade882db0d”},“signature”:“signature2”},..]}结构很容易理解。首先是payload字段,不用多说,接下来是signatures字段,是一个代表多个签名的数组。每个签名的结构如下:protected:前面的header声明,用b64uri加密;header:JWS的附加声明,此内容不会放在签名中,不需要校验;signature:即当前header+payload的标志。JWE相关概念JWE是一个很新的概念。总之,除了jwt官方手册,很少有网站或者博客会介绍这个东西。也不是所有的库都支持JWE。这里记录下我看了官方手册后的理解。JWS是为了验证数据,JWE(JSONWebEncryption)是为了保护数据不被第三方看到。有了JWE,JWT变得更加安全。JWE和JWS的公钥和私钥方案不同。在JWS中,私钥持有者加密令牌,公钥持有者验证令牌。在JWE中,私钥方应该是唯一可以解密token的一方。在JWE中,公钥持有者可以向JWT中放入新数据,而在JWS中,公钥持有者只能验证数据,不能引入新数据。因此,对于公钥/私钥方案,JWS和JWE是互补的。JWE的构成一个JWE,应该是如下形式的:eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKxYxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTPcFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A.AxY8DCtDaGlsbGljb3RoZQ.KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.9hH0vgRfYgPnAHOd8stkvw如你所见JWE一共有五个部分,分别是:protectedheader,类似JWS的header;加密密钥,用于加密密文和其他加密数据的对称密钥;初始化向量,初始IV值,一些加密方法需要附加或随机数据;加密后的数据(密文),密文数据;认证标签,由算法生成的附加数据,用于防止密文被篡改。JWE密钥加密算法一般来说,JWE需要对密钥进行加密,也就是说至少有两种加密算法在同一个JWT中工作。但是不能使用按键。我们需要对密钥进行加密,并使用JWK密钥管理方式来导出这些密钥。JWK有五种管理模式,分别是:KeyEncryptionKeyWrappingDirectKeyAgreementKeyAgreementwithKeyWrappingDirectEncryption并不是所有的JWA都能支持这五种密钥管理模式,也不是每一种密钥管理模式都可以相互转换。可以参考Spomky-Labs/jose(https://github.com/Spomky-Labs/jose/blob/master/doc/operation/Encrypt.md)中给出的表格。至于每种密钥管理方式的详细介绍,请看JWT官方手册,解释起来比较复杂。JWEHeader就像是JWS的头部。JWEheader也有自己额外的声明字段,如下:type:一般为jwtalg:算法名,和JWS一样,这个算法是用来加密后面加密内容的实际密钥enc:算法名,算法用于使用上一步中生成的密钥加密内容。zip:加密前压缩数据的算法。此参数是可选的。如果不存在,则不执行压缩。通常的值是DEF,也就是deflate算法jku/jkw/kid/x5u/x5c/x5t/x5t#S256/typ/cty/crit:和JWSamountofadditionaldeclarations一样。JWE的加密过程中的第2步和第3步的密钥管理方式不同,应该区别对待。这里只列出一些常见的情况。如前所述,JWE有五个部分。下面详细说一下加密的过程:根据headeralg的语句生成一个一定大小的随机数;根据密钥管理方式确定加密密钥;根据密钥管理方式确定JWE加密密钥,获取CEK;计算初始IV,如果不需要,跳过这一步;如果ZIP头声明,则压缩明文;使用CEK、IV和附加认证数据,通过enc头中声明的算法对内容进行加密,结果为加密数据和认证标志;压缩内容,返回令牌。base64(header)+'.'+base64(encryptedKey)+'.'+//Steps2and3base64(initializationVector)+'.'+//Step4base64(ciphertext)+'.'+//Step6base64(authenticationTag)//Step6多个与JWE序列化和JWS类似,JWE也为多种加密形式定义了一种紧凑的序列化格式。大致格式如下:{"protected":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0","unprotected":{"jku":"https://server.example.com/keys.jwks"},"recipients":[{"header":{"alg":"RSA1_5","kid":"2011-04-29"},"encrypted_key":"UGhIOguC7Iu...cqXMR4gp_A"},{"header":{"alg":"A128KW","kid":"7"},"encrypted_key":"6KB707dM9YTIgH...9locizkDTHzBC2IlrT1oOQ"}],"iv":"AxY8DCtDaGlsbGljb3RoZQ","ciphertext":"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY","tag":"Mz-VPPyU4RlcuYv1IwIvzw"}结构很容易理解,如下:protected:前面的header语句,用b64uri加密;unprotected:一般放JWS的额外声明,这个内容不会被b64加密;iv:64位加密后的iv参数;添加:额外的身份验证数据;密文:b64加密后的加密数据;recipients:b64加密后的认证标志-加密链,是一个数组,每个数组包含两条信息;header:主要声明当前key的算法;encrypted_key:JWE加密密钥。JWT的工作原理下面介绍一下jwt是如何通过juiceshop进行工作的。在身份验证中,当用户使用其凭据成功登录时,将返回一个JSONWebToken。如下图:此时返回了jwttoken。每当用户想要访问受保护的路由或资源时,用户将使用承载方式发送JWT,通常在Authorization标头中。title的内容应该是这样的:Authorization:Bearer
