当前位置: 首页 > 后端技术 > Java

java开发中JWT、JWE、JWS、JWK都是干啥的

时间:2023-04-01 23:55:22 Java

什么是JWT一个JWT,应该是如下形式的:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ这些东西看上很凌乱,但是非常紧凑,并且是可打印ismainlyusedtoverifytheauthenticityofthesignature.WhatproblemdoesJWTsolve?ThemainpurposeofJWTistotransferclaimsbetweenserverandclientinasecuremanner.Themainapplicationscenariosofjavatrainingareasfollows:authenticationAuthentication;AuthorizationAuthorization//Payattentiontothedifferencebetweenthesetwowords;jointidentification;clientsession(statelesssession);clientconfidentiality.SometermsofJWTexplainJWS:SignedJWTsignedjwtJWE:EncryptedJWTpartofthepayloadencryptedjwt;currentlytheoperationofencryptingpayloadisnotverypopular;JWK:JWTkey,whichiswhatweoftencallsecret;JWKset:JWTInasymmetricencryption,keysetrequiresakeypairratherthanaseparatekey,whichwillbeexplainedlater;JWA:thecryptographicalgorithmusedbythecurrentJWT;nonsecureJWT:whenthesignaturealgorithmoftheheaderissetWhenitisnone,theJWTisinsecure;becausethesignaturepartisblank,everyonecanmodifyit.ThecompositionofJWTAjwtthatyouusuallyseeiscomposedofthefollowingthreeparts,whichare:header:mainlydeclaresthesignaturealgorithmofJWT;payload:mainlycarriesvariousdeclarationsandtransmitsplaintextdata;signature:ownsthispartJWTiscalledJWS,thatis,signedJWS;JWTwithoutthispartiscallednonsecureJWT,thatis,unsafeJWT.Atthistime,thesignaturealgorithmdeclaredintheheaderisnone.Thethreepartsaredividedby·.Astylelikexxxxx.yyyyy.zzzzz.JWTheader{"typ":"JWT","alg":"none","jti":"4f1g23a12aa"}Thecompositionheaderofthejwtheaderusuallyconsistsoftwoparts:thetypeoftoken,whichisJWT,andthetypeoftokenbeingusedAhashingalgorithmsuchasHMACSHA256orRSA.当然还有两个可选的部分,一个是jti,也就是JWTID,代表正在使用的JWT的编号,这个编号在对应的服务器上应该是唯一的。当然jti也可以放在payload里面。另一个是cty,也就是内容类型。这是比较少见的。当payload是任意数据时,这个header不需要设置,但是当content中也包含jwt时。即在嵌套JWT时,这个值必须设置为jwt。这种情况比较少见。jwtheader加密算法如下:base64UrlEncode(header)eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIiwianRpIjoiNGYxZzIzYTEyYWEifQJWTpayload{"iss":"http://shaobaobaoer.cn","aud":"http://shaobaobaoer.cn/webtest/jwt_"jti":"4f1g23a12aa","iat":1534070547,"nbf":1534070607,"exp":1534074147,"uid":1,"data":{"uname":"shaobao","uEmail":"shaobaobaoer@126.com","uID":"0xA0","uGroup":"guest"}}jwtpayload组成payload通常由三部分组成,分别是RegisteredClaims;PublicClaims;PrivateClaims;每一个claim,每一个都有它的自己的领域。RegisteredClaims?iss[issuer]issuer的url地址?sub[subject]JWT所针对的用户,用于处理特定的应用,不是常用的字段?aud[audience]接收者的url地址?exp[expiration]jwt被销毁的时间;unixtimestampnbf[notbefore]jwt不能早于这个时间使用;unixtimestampiat[issuedat]jwt的发布时间;unixtimestampjti[JWTID]theuniqueIDnumberofjwtPublicClaims这些可以由那些使用JWT的标准化组织根据需要定义,应该参考文档IANAJSONWebTokenRegistry。私人声明这些是为在同意使用它们的各方之间共享信息而创建的自定义声明,既不是注册声明也不是公共声明。在上面的有效负载中,没有公共声明,只有私有声明。jwtpayload的加密算法加密的方式如下:base64UrlEncode(payload)eyJpc3MiOiJodHRwOi8vc2hhb2Jhb2Jhb2VyLmNuIiwiYXVkIjoiaHR0cDovL3NoYW9iYW9iYW9lci5jbi93ZWJ0ZXN0L2p3dF9hdXRoLyIsImp0aSI6IjRmMWcyM2ExMmFhIiwiaWF0IjoxNTM0MDcwNTQ3LCJuYmYiOjE1MzQwNzA2MDcsImV4cCI6MTUzNDA3NDE0NywidWlkIjoxLCJkYXRhIjp7InVuYW1lIjoic2hhb2JhbyIsInVFbWFpbCI6InNoYW9iYW9iYW9lckAxMjYuY29tIiwidUlEIjoiMHhBMCIsInVHcm91cCI6Imd1ZXN0In19暴露的信息所以,在JWT中,不应该在载荷里面加入任何敏感的数据。Intheaboveexample,wearetransmittingtheuser'sUserID,emailaddress等。这个值其实并不敏感,知道一般是安全的。但是密码等内容不能放在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有3种签名算法。?对称加密HMAC【哈希消息认证码】:HS256/HS384/HS512?非对称加密RSASSA【RSA签名算法】(RS256/RS384/RS512)?ECDSA【椭圆曲线数据签名算法】(ES256/ES384/ES512)最后,将签名与前两段连接起来。得到签名的JWT,也就是JWS。eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImp0aSI6IjRmMWcyM2ExMmFhIn0.eyJpc3MiOiJodHRwOi8vc2hhb2Jhb2Jhb2VyLmNuIiwiYXVkIjoiaHR0cDovL3NoYW9iYW9iYW9lci5jbi93ZWJ0ZXN0L2p3dF9hdXRoLyIsImp0aSI6IjRmMWcyM2ExMmFhIiwiaWF0IjoxNTM0MDcwNTQ3LCJuYmYiOjE1MzQwNzA2MDcsImV4cCI6MTUzNDA3NDE0NywidWlkIjoxLCJkYXRhIjp7InVuYW1lIjoic2hhb2JhbyIsInVFbWFpbCI6InNoYW9iYW9iYW9lckAxMjYuY29tIiwidUlEIjoiMHhBMCIsInVHcm91cCI6Imd1ZXN0In19.GQPGEpixjPZSZ7CmqXB-KIGNzNl4Y86d3XOaRsfiXmQ当验证签名的时候,利用公钥或者密钥来解密Sign,和base64UrlEncode(header)+"."+base64UrlEncode(payload)的内容完全一样的时候,表示验证通过。IfyouhavesomeconceptsaboutCAintheextraheaderdeclarationofJWS,thesecontentswillbeeasiertounderstand.Inordertoensurethattheserver'skeypairisreliableandvalid,anditisalsoconvenientforthird-partyCAagenciestosignJWTinsteadofthelocalservertosignJWT,therecanbeadditionaldeclarationsfortheJWSheader.Thefollowingdeclarationsareoptional,dependingonHowtouseJWS.Itisasfollows:?jku:TheaddressofsendingJWK;itisbesttouseHTTPStotransmit?jwk:ItistheJWKmentionedearlier?kid:IDnumberofjwk?x5u:URLpointingtoasetofX509publiccertificates?x5c:X509certificatechain?x5t:SHA-1fingerprintofX509certificate?x5t#S256:SHA-256fingerprintofX509certificate?typ:JOSEandJOSE+JSONareaddedtotheoriginalunencryptedJWT.JOSEserializationwillbementionedlater.AppliestothecasewheretheJOSEheader'sobjectismixedwiththisJWT.?crit:arrayofstringscontainingthenamesoftheclaims,usedasimplementation-definedextensions,thatmustbeprocessedbythis->JWT'sparser.Unusual.多重身份验证和JWS序列化当多重签名或JOSE标头对象与JWS混合时,通常需要JWS序列化。JWS的序列化结构如下所示:{"payload":"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ","signatures":[{"protected":"eyJhbGciOiJSUzI1NiJ9","header":{"kid":"2010-12-29"},“签名”:“签名1”},{“受保护”:“eyJhbGciOiJSUzI1NiJ9”,“标题”:{“孩子”:“e9bc097a-ce51-4036-9562-d2ade882db0d”},“签名”:“签名2”},..]}结构很容易理解。首先是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一共有五个部分,分别是:?Theprotectedheader,similartotheJWSheader;?Theencryptedkey,thesymmetrickeyusedtoencryptciphertextandotherencrypteddata;?Theinitializationvector,theinitialIVvalue,someencryptionmethodsrequireadditionalorrandomdata;?Theencrypteddata(ciphertext),ciphertextdata;?Theauthenticationtag,additionaldatageneratedbythealgorithmtopreventtheciphertextfrombeingtamperedwith.JWEkeyencryptionalgorithmGenerallyspeaking,JWEneedstoencryptthekey,whichmeansthatatleasttwoencryptionalgorithmsareworkinginthesameJWT.Butitisnotpossibletousethekeys.WeneedtoencryptthekeysandusetheJWKkeymanagementmodetoderivethesekeys.JWK有五种管理模式,分别是:?KeyEncryption?KeyWrapping?DirectKeyAgreement?KeyAgreementwithKeyWrapping?DirectEncryption不是所有的JWA都能支持这五种密钥管理模式,也不是每一种密钥管理模式都可以转换对彼此。至于每种密钥管理方式的细节,请参考JWT官方手册,解释起来比较复杂。JWEHeader就像是JWS的头部。JWE的header也有自己额外声明的字段,如下:?type:一般为jwt?alg:算法名,和JWS一样,这个算法是用来加密后面加密内容时使用的实际密钥?enc:Algorithm名称,用于使用上一步生成的密钥加密内容的算法。?zip:加密前压缩数据的算法。此参数是可选的。如果不存在,则不会进行压缩。通常的值是DEF,也就是deflate算法。同样的声明。JWE的加密过程中的第2步和第3步的密钥管理方式不同,应该区别对待。这里只列出一些常见的情况。如前所述,JWE有五个部分。下面详细说一下加密过程:根据headeralg语句,生成一个一定大小的随机数;根据密钥管理方式确定加密密钥;根据密钥管理方式确定JWE加密密钥,得到CEK;计算初始IV,如果不需要,跳过这一步;如果ZIP标头声明,则压缩明文;使用CEK、IV和附加认证数据,通过enc头中声明的算法对内容进行加密,结果为加密数据和认证标志;压缩内容,返回令牌。base64(header)+'.'+base64(encryptedKey)+'.'+//步骤2和3base64(initializationVector)+'.'+//步骤4base64(密文)+'.'+//Step6base64(authenticationTag)//Step6Multi-factorauthentication类似于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之后服务器会取出token中的内容,返回相应的内容。需要注意的是,这个令牌可能不会存储在cookie中。如果cookie中存在,需要设置为http-only,防止XSS。另外,也可以放在其他地方,比如localStorage,sessionStorage。如果用vue,也可以存到vuex中。此外,如果您以Authorization:Bearer之类的方式发送令牌,跨源资源共享(CORS)将不会成为问题,因为它不使用cookie。此时访问认证页面,请求头如下。不出所料,使用Authorization:Bearer的请求头进行访问。ECDSA|RSASSA还是HMAC?我应该选择哪个?首先要明确的是,无论使用HMAC、RSASSA、ECDSA;密钥、公钥和私钥不会发送给客户端,只会保存在服务器端。对称算法HMAC适用于单点登录和一对一场景。高速。但是在一对多的情况下,比如一个APP中不同的服务模块,需要JWT登录时,主服务器【APP】有私钥完成签名,用户访问不同的服务模块用JWT[Sub-server],子服务器只需要使用公钥来验证签名。也在一定程度上减轻了主服务器的压力。当然还有一种情况,就是不同成员在开发的时候,大家可以使用统一的私钥完成签名,然后使用各自的公钥完成JWT认证,这也是一种很好的开发方式。因此,要构建一个没有多个小型“微服务应用程序”且只有一组开发人员的应用程序,请选择HMAC进行签名。其他情况尽量选择RSA。作者:第九恶魔猎手