<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于jwt的token驗(yàn)證、原理及流程

          共 8624字,需瀏覽 18分鐘

           ·

          2021-09-23 05:09

          來(lái)源:www.cnblogs.com/better-farther-world2099


          一、什么是JWT

          Json web token (JWT), 是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開(kāi)放標(biāo)準(zhǔn)((RFC 7519).

          該token被設(shè)計(jì)為緊湊且安全的,特別適用于分布式站點(diǎn)的單點(diǎn)登錄(SSO)場(chǎng)景。

          JWT的聲明一般被用來(lái)在身份提供者和服務(wù)提供者間傳遞被認(rèn)證的用戶身份信息,以便于從資源服務(wù)器獲取資源,也可以增加一些額外的其它業(yè)務(wù)邏輯所必須的聲明信息,該token也可直接被用于認(rèn)證,也可被加密。

          二、JWT的組成

          1、JWT生成編碼后的樣子

          eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q

          2、JWT由三部分構(gòu)成

          第一部分我們稱它為頭部(header),第二部分我們稱其為載荷(payload, 類似于飛機(jī)上承載的物品),第三部分是簽證(signature).

          header

          jwt的頭部承載兩部分信息:

          • 聲明類型,這里是jwt
          • 聲明加密的算法 通常直接使用 HMAC SHA256

          完整的頭部就像下面這樣的JSON:

          {
            'typ''JWT',
            'alg''HS256'
          }

          然后將頭部進(jìn)行base64加密(該加密是可以對(duì)稱解密的),構(gòu)成了第一部分

          eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

          playload

          載荷就是存放有效信息的地方。這個(gè)名字像是特指飛機(jī)上承載的貨品,這些有效信息包含三個(gè)部分

          • 標(biāo)準(zhǔn)中注冊(cè)的聲明
          • 公共的聲明
          • 私有的聲明

          標(biāo)準(zhǔn)中注冊(cè)的聲明 (建議但不強(qiáng)制使用) :

          • iss: jwt簽發(fā)者
          • sub: jwt所面向的用戶
          • aud: 接收jwt的一方
          • exp: jwt的過(guò)期時(shí)間,這個(gè)過(guò)期時(shí)間必須要大于簽發(fā)時(shí)間
          • nbf: 定義在什么時(shí)間之前,該jwt都是不可用的.
          • iat: jwt的簽發(fā)時(shí)間
          • jti: jwt的唯一身份標(biāo)識(shí),主要用來(lái)作為一次性token,從而回避重放攻擊。

          公共的聲明 :

          公共的聲明可以添加任何的信息,一般添加用戶的相關(guān)信息或其他業(yè)務(wù)需要的必要信息.但不建議添加敏感信息,因?yàn)樵摬糠衷诳蛻舳丝山饷?

          私有的聲明 :

          私有聲明是提供者和消費(fèi)者所共同定義的聲明,一般不建議存放敏感信息,因?yàn)閎ase64是對(duì)稱解密的,意味著該部分信息可以歸類為明文信息。

          定義一個(gè)payload:

          {
            "sub""1234567890",
            "name""John Doe",
            "admin"true
          }

          然后將其進(jìn)行base64加密,得到Jwt的第二部分

          eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

          signature

          jwt的第三部分是一個(gè)簽證信息,這個(gè)簽證信息由三部分組成:

          • header (base64后的)
          • payload (base64后的)
          • secret

          這個(gè)部分需要base64加密后的header和base64加密后的payload使用.連接組成的字符串(頭部在前),然后通過(guò)header中聲明的加密方式進(jìn)行加鹽secret組合加密,然后就構(gòu)成了jwt的第三部分。

          UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q

          密鑰secret是保存在服務(wù)端的,服務(wù)端會(huì)根據(jù)這個(gè)密鑰進(jìn)行生成token和驗(yàn)證,所以需要保護(hù)好。

          3、簽名的目的

          最后一步簽名的過(guò)程,實(shí)際上是對(duì)頭部以及載荷內(nèi)容進(jìn)行簽名。一般而言,加密算法對(duì)于不同的輸入產(chǎn)生的輸出總是不一樣的。對(duì)于兩個(gè)不同的輸入,產(chǎn)生同樣的輸出的概率極其地小(有可能比我成世界首富的概率還小)。所以,我們就把“不一樣的輸入產(chǎn)生不一樣的輸出”當(dāng)做必然事件來(lái)看待吧。

          所以,如果有人對(duì)頭部以及載荷的內(nèi)容解碼之后進(jìn)行修改,再進(jìn)行編碼的話,那么新的頭部和載荷的簽名和之前的簽名就將是不一樣的。而且,如果不知道服務(wù)器加密的時(shí)候用的密鑰的話,得出來(lái)的簽名也一定會(huì)是不一樣的。

          服務(wù)器應(yīng)用在接受到JWT后,會(huì)首先對(duì)頭部和載荷的內(nèi)容用同一算法再次簽名。那么服務(wù)器應(yīng)用是怎么知道我們用的是哪一種算法呢?別忘了,我們?cè)贘WT的頭部中已經(jīng)用alg字段指明了我們的加密算法了。

          如果服務(wù)器應(yīng)用對(duì)頭部和載荷再次以同樣方法簽名之后發(fā)現(xiàn),自己計(jì)算出來(lái)的簽名和接受到的簽名不一樣,那么就說(shuō)明這個(gè)Token的內(nèi)容被別人動(dòng)過(guò)的,我們應(yīng)該拒絕這個(gè)Token,返回一個(gè)HTTP 401 Unauthorized響應(yīng)。

          注意:在JWT中,不應(yīng)該在載荷里面加入任何敏感的數(shù)據(jù),比如用戶的密碼。

          4、如何應(yīng)用

          一般是在請(qǐng)求頭里加入Authorization,并加上Bearer標(biāo)注:

          fetch('api/user/1', {
            headers: {
              'Authorization''Bearer ' + token
            }
          })

          服務(wù)端會(huì)驗(yàn)證token,如果驗(yàn)證通過(guò)就會(huì)返回相應(yīng)的資源。

          5、安全相關(guān)

          • 不應(yīng)該在jwt的payload部分存放敏感信息,因?yàn)樵摬糠质强蛻舳丝山饷艿牟糠帧?/section>
          • 保護(hù)好secret私鑰,該私鑰非常重要。
          • 如果可以,請(qǐng)使用https協(xié)議

          6、對(duì)Token認(rèn)證的五點(diǎn)認(rèn)識(shí)

          • 一個(gè)Token就是一些信息的集合;
          • 在Token中包含足夠多的信息,以便在后續(xù)請(qǐng)求中減少查詢數(shù)據(jù)庫(kù)的幾率;
          • 服務(wù)端需要對(duì)cookie和HTTP Authrorization Header進(jìn)行Token信息的檢查;
          • 基于上一點(diǎn),你可以用一套token認(rèn)證代碼來(lái)面對(duì)瀏覽器類客戶端和非瀏覽器類客戶端;
          • 因?yàn)閠oken是被簽名的,所以我們可以認(rèn)為一個(gè)可以解碼認(rèn)證通過(guò)的token是由我們系統(tǒng)發(fā)放的,其中帶的信息是合法有效的;

          三、傳統(tǒng)的session認(rèn)證

          我們知道,http協(xié)議本身是一種無(wú)狀態(tài)的協(xié)議,而這就意味著如果用戶向我們的應(yīng)用提供了用戶名和密碼來(lái)進(jìn)行用戶認(rèn)證,那么下一次請(qǐng)求時(shí),用戶還要再一次進(jìn)行用戶認(rèn)證才行,因?yàn)楦鶕?jù)http協(xié)議,我們并不能知道是哪個(gè)用戶發(fā)出的請(qǐng)求,所以為了讓我們的應(yīng)用能識(shí)別是哪個(gè)用戶發(fā)出的請(qǐng)求,我們只能在服務(wù)器存儲(chǔ)一份用戶登錄的信息,這份登錄信息會(huì)在響應(yīng)時(shí)傳遞給瀏覽器,告訴其保存為cookie,以便下次請(qǐng)求時(shí)發(fā)送給我們的應(yīng)用,這樣我們的應(yīng)用就能識(shí)別請(qǐng)求來(lái)自哪個(gè)用戶了,這就是傳統(tǒng)的基于session認(rèn)證。

          但是這種基于session的認(rèn)證使應(yīng)用本身很難得到擴(kuò)展,隨著不同客戶端用戶的增加,獨(dú)立的服務(wù)器已無(wú)法承載更多的用戶,而這時(shí)候基于session認(rèn)證應(yīng)用的問(wèn)題就會(huì)暴露出來(lái)。

          基于session認(rèn)證所顯露的問(wèn)題

          Session: 每個(gè)用戶經(jīng)過(guò)我們的應(yīng)用認(rèn)證之后,我們的應(yīng)用都要在服務(wù)端做一次記錄,以方便用戶下次請(qǐng)求的鑒別,通常而言session都是保存在內(nèi)存中,而隨著認(rèn)證用戶的增多,服務(wù)端的開(kāi)銷會(huì)明顯增大。

          擴(kuò)展性: 用戶認(rèn)證之后,服務(wù)端做認(rèn)證記錄,如果認(rèn)證的記錄被保存在內(nèi)存中的話,這意味著用戶下次請(qǐng)求還必須要請(qǐng)求在這臺(tái)服務(wù)器上,這樣才能拿到授權(quán)的資源,這樣在分布式的應(yīng)用上,相應(yīng)的限制了負(fù)載均衡器的能力。這也意味著限制了應(yīng)用的擴(kuò)展能力。

          CSRF: 因?yàn)槭腔赾ookie來(lái)進(jìn)行用戶識(shí)別的, cookie如果被截獲,用戶就會(huì)很容易受到跨站請(qǐng)求偽造的攻擊。

          基于token的鑒權(quán)機(jī)制

          基于token的鑒權(quán)機(jī)制類似于http協(xié)議也是無(wú)狀態(tài)的,它不需要在服務(wù)端去保留用戶的認(rèn)證信息或者會(huì)話信息。這就意味著基于token認(rèn)證機(jī)制的應(yīng)用不需要去考慮用戶在哪一臺(tái)服務(wù)器登錄了,這就為應(yīng)用的擴(kuò)展提供了便利。

          流程上是這樣的:

          • 用戶使用用戶名密碼來(lái)請(qǐng)求服務(wù)器
          • 服務(wù)器進(jìn)行驗(yàn)證用戶的信息
          • 服務(wù)器通過(guò)驗(yàn)證發(fā)送給用戶一個(gè)token
          • 客戶端存儲(chǔ)token,并在每次請(qǐng)求時(shí)附送上這個(gè)token值
          • 服務(wù)端驗(yàn)證token值,并返回?cái)?shù)據(jù)

          這個(gè)token必須要在每次請(qǐng)求時(shí)傳遞給服務(wù)端,它應(yīng)該保存在請(qǐng)求頭里, 另外,服務(wù)端要支持CORS(跨來(lái)源資源共享)策略,一般我們?cè)诜?wù)端這么做就可以了 Access-Control-Allow-Origin:*。

          四、token的優(yōu)點(diǎn)

          • 支持跨域訪問(wèn): Cookie是不允許垮域訪問(wèn)的,這一點(diǎn)對(duì)Token機(jī)制是不存在的,前提是傳輸?shù)挠脩粽J(rèn)證信息通過(guò)HTTP頭傳輸。
          • 無(wú)狀態(tài)(也稱:服務(wù)端可擴(kuò)展行):Token機(jī)制在服務(wù)端不需要存儲(chǔ)session信息,因?yàn)門oken 自身包含了所有登錄用戶的信息,只需要在客戶端的cookie或本地介質(zhì)存儲(chǔ)狀態(tài)信息。
          • 更適用CDN: 可以通過(guò)內(nèi)容分發(fā)網(wǎng)絡(luò)請(qǐng)求你服務(wù)端的所有資料(如:javascript,HTML,圖片等),而你的服務(wù)端只要提供API即可。
          • 去耦: 不需要綁定到一個(gè)特定的身份驗(yàn)證方案。Token可以在任何地方生成,只要在你的API被調(diào)用的時(shí)候,你可以進(jìn)行Token生成調(diào)用即可。
          • 更適用于移動(dòng)應(yīng)用: 當(dāng)你的客戶端是一個(gè)原生平臺(tái)(iOS, Android,Windows 8等)時(shí),Cookie是不被支持的(你需要通過(guò)Cookie容器進(jìn)行處理),這時(shí)采用Token認(rèn)證機(jī)制就會(huì)簡(jiǎn)單得多。
          • CSRF:因?yàn)椴辉僖蕾囉贑ookie,所以你就不需要考慮對(duì)CSRF(跨站請(qǐng)求偽造)的防范。
          • 性能: 一次網(wǎng)絡(luò)往返時(shí)間(通過(guò)數(shù)據(jù)庫(kù)查詢session信息)總比做一次HMACSHA256計(jì)算 的Token驗(yàn)證和解析要費(fèi)時(shí)得多。
          • 不需要為登錄頁(yè)面做特殊處理: 如果你使用Protractor 做功能測(cè)試的時(shí)候,不再需要為登錄頁(yè)面做特殊處理。
          • 基于標(biāo)準(zhǔn)化:你的API可以采用標(biāo)準(zhǔn)化的 JSON Web Token (JWT). 這個(gè)標(biāo)準(zhǔn)已經(jīng)存在多個(gè)后端庫(kù)(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft)。
          • 因?yàn)閖son的通用性,所以JWT是可以進(jìn)行跨語(yǔ)言支持的,像JAVA,JavaScript,NodeJS,PHP等很多語(yǔ)言都可以使用。
          • 因?yàn)橛辛藀ayload部分,所以JWT可以在自身存儲(chǔ)一些其他業(yè)務(wù)邏輯所必要的非敏感信息。
          • 便于傳輸,jwt的構(gòu)成非常簡(jiǎn)單,字節(jié)占用很小,所以它是非常便于傳輸?shù)摹?/section>
          • 它不需要在服務(wù)端保存會(huì)話信息, 所以它易于應(yīng)用的擴(kuò)展。

          五、JWT的JAVA實(shí)現(xiàn)

          Java中對(duì)JWT的支持可以考慮使用JJWT開(kāi)源庫(kù);JJWT實(shí)現(xiàn)了JWT, JWS, JWE 和 JWA RFC規(guī)范;

          下面將簡(jiǎn)單舉例說(shuō)明其使用:

          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 token
          SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
           
          long nowMillis = System.currentTimeMillis();
          Date now = new Date(nowMillis);
           
          //We will sign our JWT with our ApiKey secret
          byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(apiKey.getSecret());
          Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
           
            //Let's set the JWT Claims
          JwtBuilder builder = Jwts.builder().setId(id)
                                          .setIssuedAt(now)
                                          .setSubject(subject)
                                          .setIssuer(issuer)
                                          .signWith(signatureAlgorithm, signingKey);
           
          //if it has been specified, let's add the expiration
          if (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 string
          return builder.compact();
          }

          2、解碼和驗(yàn)證Token碼

          import javax.xml.bind.DatatypeConverter;
          import io.jsonwebtoken.Jwts;
          import io.jsonwebtoken.Claims;
           
          //Sample method to validate and read the JWT
          private 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());
          }

          文章整理自:

          • https://blog.csdn.net/buyaoshuohua1/article/details/73739419
          • https://www.cnblogs.com/xiekeli/p/5607107.html#top
          • https://blog.csdn.net/SoftwareOscar/article/details/78538346
          • https://blog.csdn.net/Jack__Frost/article/details/64964208
          • http://blog.leapoahead.com/2015/09/06/understanding-jwt/

          瀏覽 78
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产嫩草影院久久久 | 国外黄A片免费看 | 日日人人爱人人 | 亚洲免费在线 | 一级a一级a爰片免费免免水l软件 |