溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

利用JWT怎么實(shí)現(xiàn)一個(gè)單點(diǎn)登錄功能

發(fā)布時(shí)間:2021-03-24 15:46:46 來源:億速云 閱讀:189 作者:Leah 欄目:web開發(fā)

利用JWT怎么實(shí)現(xiàn)一個(gè)單點(diǎn)登錄功能?很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

什么情況下使用JWT比較適合?

  • 授權(quán):這是最常見的使用場(chǎng)景,解決單點(diǎn)登錄問題。因?yàn)镴WT使用起來輕便,開銷小,服務(wù)端不用記錄用戶狀態(tài)信息(無狀態(tài)),所以使用比較廣泛;

  • 信息交換:JWT是在各個(gè)服務(wù)之間安全傳輸信息的好方法。因?yàn)镴WT可以簽名,例如,使用公鑰/私鑰對(duì)兒 - 可以確定請(qǐng)求方是合法的。此外,由于使用標(biāo)頭和有效負(fù)載計(jì)算簽名,還可以驗(yàn)證內(nèi)容是否未被篡改。

JWT的結(jié)構(gòu)體是什么樣的?

JWT由三部分組成,分別是頭信息、有效載荷、簽名,中間以(.)分隔,如下格式:

xxx.yyy.zzz

header(頭信息)

由兩部分組成,令牌類型(即:JWT)、散列算法(HMAC、RSASSA、RSASSA-PSS等),例如:

{
 "alg": "HS256",
 "typ": "JWT"
}

然后,這個(gè)JSON被編碼為Base64Url,形成JWT的第一部分。

Payload(有效載荷)

JWT的第二部分是payload,其中包含claims。claims是關(guān)于實(shí)體(常用的是用戶信息)和其他數(shù)據(jù)的聲明,claims有三種類型: registered, public, and private claims。

Registered claims: 這些是一組預(yù)定義的claims,非強(qiáng)制性的,但是推薦使用, iss(發(fā)行人), exp(到期時(shí)間), sub(主題), aud(觀眾)等;

Public claims: 自定義claims,注意不要和JWT注冊(cè)表中屬性沖突,這里可以查看JWT注冊(cè)表

Private claims: 這些是自定義的claims,用于在同意使用這些claims的各方之間共享信息,它們既不是Registered claims,也不是Public claims。

以下是payload示例:

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

然后,再經(jīng)過Base64Url編碼,形成JWT的第二部分;

注意:對(duì)于簽名令牌,此信息雖然可以防止篡改,但任何人都可以讀取。除非加密,否則不要將敏感信息放入到Payload或Header元素中。

Signature

要?jiǎng)?chuàng)建簽名部分,必須采用編碼的Header,編碼的Payload,秘鑰,Header中指定的算法,并對(duì)其進(jìn)行簽名。

例如,如果要使用HMAC SHA256算法,將按以下方式創(chuàng)建簽名:

HMACSHA256(
 base64UrlEncode(header) + "." +
 base64UrlEncode(payload),
 secret)

簽名用于驗(yàn)證消息在此過程中未被篡改,并且,在使用私鑰簽名令牌的情況下,它還可以驗(yàn)證JWT的請(qǐng)求方是否是它所聲明的請(qǐng)求方。

輸出是三個(gè)由點(diǎn)分隔的Base64-URL字符串,可以在HTML和HTTP環(huán)境中輕松傳遞,與SAML等基于XML的標(biāo)準(zhǔn)相比更加緊湊。
例如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

JWT工作機(jī)制?

在身份驗(yàn)證中,當(dāng)用戶使用其憑據(jù)成功登錄時(shí),將返回JSON Web Token(即:JWT)。由于令牌是憑證,因此必須非常小心以防止出現(xiàn)安全問題。一般情況下,不應(yīng)將令牌保留的時(shí)間超過要求。理論上超時(shí)時(shí)間越短越好。

每當(dāng)用戶想要訪問受保護(hù)的路由或資源時(shí),用戶代理應(yīng)該使用Bearer模式發(fā)送JWT,通常在Authorization header中。標(biāo)題內(nèi)容應(yīng)如下所示:

Authorization: Bearer <token>

在某些情況下,這可以作為無狀態(tài)授權(quán)機(jī)制。服務(wù)器的受保護(hù)路由將檢查Authorization header中的有效JWT ,如果有效,則允許用戶訪問受保護(hù)資源。如果JWT包含必要的數(shù)據(jù),則可以減少查詢數(shù)據(jù)庫或緩存信息。
如果在Authorization header中發(fā)送令牌,則跨域資源共享(CORS)將不會(huì)成為問題,因?yàn)樗皇褂胏ookie。

注意:使用簽名令牌,雖然他們無法更改,但是令牌中包含的所有信息都會(huì)向用戶或其他方公開。這意味著不應(yīng)該在令牌中放置敏感信息。

使用JWT的好處是什么?

相比Simple Web Tokens (SWT)(簡(jiǎn)單Web令牌) and Security Assertion Markup Language Tokens (SAML)(安全斷言標(biāo)記語言令牌);

  • JWT比SAML更簡(jiǎn)潔,在HTML和HTTP環(huán)境中傳遞更方便;

  • 在安全方面,SWT只能使用HMAC算法通過共享密鑰對(duì)稱簽名。但是,JWT和SAML令牌可以使用X.509證書形式的公鑰/私鑰對(duì)進(jìn)行簽名。與簽名JSON的簡(jiǎn)單性相比,使用XML數(shù)字簽名可能會(huì)存在安全漏洞;

  • JSON解析成對(duì)象相比XML更流行、方便。

以下是我實(shí)際項(xiàng)目中的應(yīng)用分析

首先看一下大致的架構(gòu)及流程圖:

利用JWT怎么實(shí)現(xiàn)一個(gè)單點(diǎn)登錄功能

利用JWT怎么實(shí)現(xiàn)一個(gè)單點(diǎn)登錄功能

主要有以下三步:

項(xiàng)目一開始我先封裝了一個(gè)JWTHelper工具包(GitHub下載),主要提供了生成JWT、解析JWT以及校驗(yàn)JWT的方法,其他還有一些加密相關(guān)操作,稍后我會(huì)以代碼的形式介紹下代碼。工具包寫好后我將打包上傳到私服,能夠隨時(shí)依賴下載使用;

接下來,我在客戶端項(xiàng)目中依賴JWTHelper工具包,并添加Interceptor攔截器,攔截需要校驗(yàn)登錄的接口。攔截器中校驗(yàn)JWT有效性,并在response中重新設(shè)置JWT的新值;

最后在JWT服務(wù)端,依賴JWT工具包,在登錄方法中,需要在登錄校驗(yàn)成功后調(diào)用生成JWT方法,生成一個(gè)JWT令牌并且設(shè)置到response的header中。

以下是部分代碼分享:

JwtHelper工具類:

/**
 * @Author: Helon
 * @Description: JWT工具類
 * 參考官網(wǎng):https://jwt.io/
 * JWT的數(shù)據(jù)結(jié)構(gòu)為:A.B.C三部分?jǐn)?shù)據(jù),由字符點(diǎn)"."分割成三部分?jǐn)?shù)據(jù)
 * A-header頭信息
 * B-payload 有效負(fù)荷 一般包括:已注冊(cè)信息(registered claims),公開數(shù)據(jù)(public claims),私有數(shù)據(jù)(private claims)
 * C-signature 簽名信息 是將header和payload進(jìn)行加密生成的
 * @Data: Created in 2018/7/19 14:11
 * @Modified By:
 */
public class JwtHelper {

  private static Logger logger = LoggerFactory.getLogger(JwtHelper.class);

  /**
   * @Author: Helon
   * @Description: 生成JWT字符串
   * 格式:A.B.C
   * A-header頭信息
   * B-payload 有效負(fù)荷
   * C-signature 簽名信息 是將header和payload進(jìn)行加密生成的
   * @param userId - 用戶編號(hào)
   * @param userName - 用戶名
   * @param identities - 客戶端信息(變長參數(shù)),目前包含瀏覽器信息,用于客戶端攔截器校驗(yàn),防止跨域非法訪問
   * @Data: 2018/7/28 19:26
   * @Modified By:
   */
  public static String generateJWT(String userId, String userName, String ...identities) {
    //簽名算法,選擇SHA-256
    SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
    //獲取當(dāng)前系統(tǒng)時(shí)間
    long nowTimeMillis = System.currentTimeMillis();
    Date now = new Date(nowTimeMillis);
    //將BASE64SECRET常量字符串使用base64解碼成字節(jié)數(shù)組
    byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SecretConstant.BASE64SECRET);
    //使用HmacSHA256簽名算法生成一個(gè)HS256的簽名秘鑰Key
    Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
    //添加構(gòu)成JWT的參數(shù)
    Map<String, Object> headMap = new HashMap<>();
    /*
      Header
      {
       "alg": "HS256",
       "typ": "JWT"
      }
     */
    headMap.put("alg", SignatureAlgorithm.HS256.getValue());
    headMap.put("typ", "JWT");
    JwtBuilder builder = Jwts.builder().setHeader(headMap)
        /*
          Payload
          {
           "userId": "1234567890",
           "userName": "John Doe",
          }
         */
        //加密后的客戶編號(hào)
        .claim("userId", AESSecretUtil.encryptToStr(userId, SecretConstant.DATAKEY))
        //客戶名稱
        .claim("userName", userName)
        //客戶端瀏覽器信息
        .claim("userAgent", identities[0])
        //Signature
        .signWith(signatureAlgorithm, signingKey);
    //添加Token過期時(shí)間
    if (SecretConstant.EXPIRESSECOND >= 0) {
      long expMillis = nowTimeMillis + SecretConstant.EXPIRESSECOND;
      Date expDate = new Date(expMillis);
      builder.setExpiration(expDate).setNotBefore(now);
    }
    return builder.compact();
  }

  /**
   * @Author: Helon
   * @Description: 解析JWT
   * 返回Claims對(duì)象
   * @param jsonWebToken - JWT
   * @Data: 2018/7/28 19:25
   * @Modified By:
   */
  public static Claims parseJWT(String jsonWebToken) {
    Claims claims = null;
    try {
      if (StringUtils.isNotBlank(jsonWebToken)) {
        //解析jwt
        claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(SecretConstant.BASE64SECRET))
            .parseClaimsJws(jsonWebToken).getBody();
      }else {
        logger.warn("[JWTHelper]-json web token 為空");
      }
    } catch (Exception e) {
      logger.error("[JWTHelper]-JWT解析異常:可能因?yàn)閠oken已經(jīng)超時(shí)或非法token");
    }
    return claims;
  }

  /**
   * @Author: Helon
   * @Description: 校驗(yàn)JWT是否有效
   * 返回json字符串的demo:
   * {"freshToken":"A.B.C","userName":"Judy","userId":"123", "userAgent":"xxxx"}
   * freshToken-刷新后的jwt
   * userName-客戶名稱
   * userId-客戶編號(hào)
   * userAgent-客戶端瀏覽器信息
   * @param jsonWebToken - JWT
   * @Data: 2018/7/24 15:28
   * @Modified By:
   */
  public static String validateLogin(String jsonWebToken) {
    Map<String, Object> retMap = null;
    Claims claims = parseJWT(jsonWebToken);
    if (claims != null) {
      //解密客戶編號(hào)
      String decryptUserId = AESSecretUtil.decryptToStr((String)claims.get("userId"), SecretConstant.DATAKEY);
      retMap = new HashMap<>();
      //加密后的客戶編號(hào)
      retMap.put("userId", decryptUserId);
      //客戶名稱
      retMap.put("userName", claims.get("userName"));
      //客戶端瀏覽器信息
      retMap.put("userAgent", claims.get("userAgent"));
      //刷新JWT
      retMap.put("freshToken", generateJWT(decryptUserId, (String)claims.get("userName"), (String)claims.get("userAgent"), (String)claims.get("domainName")));
    }else {
      logger.warn("[JWTHelper]-JWT解析出claims為空");
    }
    return retMap!=null?JSONObject.toJSONString(retMap):null;
  }

  public static void main(String[] args) {
    String jsonWebKey = generateJWT("123", "Judy",
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36");
    System.out.println(jsonWebKey);
    Claims claims = parseJWT(jsonWebKey);
    System.out.println(claims);
    System.out.println(validateLogin(jsonWebKey));
  }

AES加密工具類:

/**
 * @Author: Helon
 * @Description: AES加密工具類
 * @Data: Created in 2018/7/28 18:38
 * @Modified By:
 */
public class AESSecretUtil {

  /**秘鑰的大小*/
  private static final int KEYSIZE = 128;
  
  /**
   * @Author: Helon
   * @Description: AES加密
   * @param data - 待加密內(nèi)容
   * @param key - 加密秘鑰
   * @Data: 2018/7/28 18:42
   * @Modified By:
   */
  public static byte[] encrypt(String data, String key) {
    if(StringUtils.isNotBlank(data)){
      try {
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        //選擇一種固定算法,為了避免不同java實(shí)現(xiàn)的不同算法,生成不同的密鑰,而導(dǎo)致解密失敗
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        random.setSeed(key.getBytes());
        keyGenerator.init(KEYSIZE, random);
        SecretKey secretKey = keyGenerator.generateKey();
        byte[] enCodeFormat = secretKey.getEncoded();
        SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
        Cipher cipher = Cipher.getInstance("AES");// 創(chuàng)建密碼器
        byte[] byteContent = data.getBytes("utf-8");
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);// 初始化
        byte[] result = cipher.doFinal(byteContent);
        return result; // 加密
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    return null;
  }

  /**
   * @Author: Helon
   * @Description: AES加密,返回String
   * @param data - 待加密內(nèi)容
   * @param key - 加密秘鑰
   * @Data: 2018/7/28 18:59
   * @Modified By:
   */
  public static String encryptToStr(String data, String key){

    return StringUtils.isNotBlank(data)?parseByte2HexStr(encrypt(data, key)):null;
  }


  /**
   * @Author: Helon
   * @Description: AES解密
   * @param data - 待解密字節(jié)數(shù)組
   * @param key - 秘鑰
   * @Data: 2018/7/28 19:01
   * @Modified By:
   */
  public static byte[] decrypt(byte[] data, String key) {
    if (ArrayUtils.isNotEmpty(data)) {
      try {
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        //選擇一種固定算法,為了避免不同java實(shí)現(xiàn)的不同算法,生成不同的密鑰,而導(dǎo)致解密失敗
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        random.setSeed(key.getBytes());
        keyGenerator.init(KEYSIZE, random);
        SecretKey secretKey = keyGenerator.generateKey();
        byte[] enCodeFormat = secretKey.getEncoded();
        SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
        Cipher cipher = Cipher.getInstance("AES");// 創(chuàng)建密碼器
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);// 初始化
        byte[] result = cipher.doFinal(data);
        return result; // 加密
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    return null;
  }

  /**
   * @Author: Helon
   * @Description: AES解密,返回String
   * @param enCryptdata - 待解密字節(jié)數(shù)組
   * @param key - 秘鑰
   * @Data: 2018/7/28 19:01
   * @Modified By:
   */
  public static String decryptToStr(String enCryptdata, String key) {
    return StringUtils.isNotBlank(enCryptdata)?new String(decrypt(parseHexStr2Byte(enCryptdata), key)):null;
  }

  /**
   * @Author: Helon
   * @Description: 將二進(jìn)制轉(zhuǎn)換成16進(jìn)制
   * @param buf - 二進(jìn)制數(shù)組
   * @Data: 2018/7/28 19:12
   * @Modified By:
   */
  public static String parseByte2HexStr(byte buf[]) {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < buf.length; i++) {
      String hex = Integer.toHexString(buf[i] & 0xFF);
      if (hex.length() == 1) {
        hex = '0' + hex;
      }
      sb.append(hex.toUpperCase());
    }
    return sb.toString();
  }

  /**
   * @Author: Helon
   * @Description: 將16進(jìn)制轉(zhuǎn)換為二進(jìn)制
   * @param hexStr - 16進(jìn)制字符串
   * @Data: 2018/7/28 19:13
   * @Modified By:
   */
  public static byte[] parseHexStr2Byte(String hexStr) {
    if (hexStr.length() < 1)
      return null;
    byte[] result = new byte[hexStr.length()/2];
    for (int i = 0;i< hexStr.length()/2; i++) {
      int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);
      int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
      result[i] = (byte) (high * 16 + low);
    }
    return result;
  }

  public static void main(String[] args) {
    String ss = encryptToStr("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiIxMjMiLCJ1c2VyTmFtZSI6Ikp1ZHkiLCJleHAiOjE1MzI3Nzk2MjIsIm5iZiI6MTUzMjc3NzgyMn0.sIw_leDZwG0pJ8ty85Iecd_VXjObYutILNEwPUyeVSo", SecretConstant.DATAKEY);
    System.out.println(ss);
    System.out.println(decryptToStr(ss, SecretConstant.DATAKEY));
  }

所使用的常量類:

/**
 * @Author: Helon
 * @Description: JWT使用常量值
 * @Data: Created in 2018/7/27 14:37
 * @Modified By:
 */
public class SecretConstant {

  //簽名秘鑰 自定義
  public static final String BASE64SECRET = "***********";

  //超時(shí)毫秒數(shù)(默認(rèn)30分鐘)
  public static final int EXPIRESSECOND = 1800000;

  //用于JWT加密的密匙 自定義
  public static final String DATAKEY = "************";

}

客戶端pom依賴:

 <!--jwt工具類-->
    <dependency>
      <groupId>com.chtwm.component</groupId>
      <artifactId>jwt-helper</artifactId>
      <version>xxx</version>
    </dependency>

客戶端攔截器:

/**
 * @Author: Helon
 * @Description: 校驗(yàn)是否登錄攔截器
 * @Data: Created in 2018/7/30 14:30
 * @Modified By:
 */
@Slf4j
public class ValidateLoginInterceptor implements HandlerInterceptor {

  @Override
  public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
    //首先從請(qǐng)求頭中獲取jwt串,與頁面約定好存放jwt值的請(qǐng)求頭屬性名為User-Token
    String jwt = httpServletRequest.getHeader("User-Token");
    log.info("[登錄校驗(yàn)攔截器]-從header中獲取的jwt為:{}", jwt);
    //判斷jwt是否有效
    if(StringUtils.isNotBlank(jwt)){
      //校驗(yàn)jwt是否有效,有效則返回json信息,無效則返回空
      String retJson = JwtHelper.validateLogin(jwt);
      log.info("[登錄校驗(yàn)攔截器]-校驗(yàn)JWT有效性返回結(jié)果:{}", retJson);
      //retJSON為空則說明jwt超時(shí)或非法
      if(StringUtils.isNotBlank(retJson)){
        JSONObject jsonObject = JSONObject.parseObject(retJson);
        //校驗(yàn)客戶端信息
        String userAgent = httpServletRequest.getHeader("User-Agent");
        if (userAgent.equals(jsonObject.getString("userAgent"))) {
          //獲取刷新后的jwt值,設(shè)置到響應(yīng)頭中
          httpServletResponse.setHeader("User-Token", jsonObject.getString("freshToken"));
          //將客戶編號(hào)設(shè)置到session中
          httpServletRequest.getSession().setAttribute(GlobalConstant.SESSION_CUSTOMER_NO_KEY, jsonObject.getString("userId"));
          return true;
        }else{
          log.warn("[登錄校驗(yàn)攔截器]-客戶端瀏覽器信息與JWT中存的瀏覽器信息不一致,重新登錄。當(dāng)前瀏覽器信息:{}", userAgent);
        }
      }else {
        log.warn("[登錄校驗(yàn)攔截器]-JWT非法或已超時(shí),重新登錄");
      }
    }
    //輸出響應(yīng)流
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("hmac", "");
    jsonObject.put("status", "");
    jsonObject.put("code", "4007");
    jsonObject.put("msg", "未登錄");
    jsonObject.put("data", "");
    httpServletResponse.setCharacterEncoding("UTF-8");
    httpServletResponse.setContentType("application/json; charset=utf-8");
    httpServletResponse.getOutputStream().write(jsonObject.toJSONString().getBytes("UTF-8"));
    return false;
  }

  @Override
  public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

  }

  @Override
  public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

  }
}

客戶端攔截器在XML文件中配置:

<!--攔截器配置-->
  <mvc:interceptors>
    <mvc:interceptor>
	    <!--需攔截url配置-->
      <mvc:exclude-mapping path="/api/aa/bb/**" />
      <mvc:exclude-mapping path="/api/aa/cc/test" />
      <bean id="validateLoginInterceptor" class="com.xxx.ValidateLoginInterceptor" />
    </mvc:interceptor>
  </mvc:interceptors>

到此,后臺(tái)服務(wù)的配置已經(jīng)完成,下一步就需要前端頁面將JWT令牌從response響應(yīng)頭中取出,然后存入localstorage或cookie中。但是遇到跨域場(chǎng)景,處理起來就會(huì)比較復(fù)雜,因?yàn)橐坏┰跒g覽器中跨域?qū)@取不到localstorage中的JWT令牌。例如www.a.com域下的JWT,在www.b.com域下是獲取不到的,所以我選擇了一種頁面跨域的方式進(jìn)行處理,使用iframe+H5的postMessage(參考博文),具體我使用代碼分享的方式來分析。

前端頁面js代碼(服務(wù)端):

 /**CURD本地存儲(chǔ)信息 start**/
     (function(doc,win){
       var fn=function(){};
       fn.prototype={
         /*本地?cái)?shù)據(jù)存儲(chǔ) t:cookie有效時(shí)間,單位s; domain:cookie存儲(chǔ)所屬的domain域*/
         setLocalCookie: function (k, v, t,domain) {
           //如果當(dāng)前瀏覽器不支持localStorage將存儲(chǔ)在cookie中
           typeof window.localStorage !== "undefined" ? localStorage.setItem(k, v) :
             (function () {
               t = t || 365 * 12 * 60 * 60;
               domain=domain?domain:".jwtserver.com";
               document.cookie = k + "=" + v + ";max-age=" + t+";domain="+domain+";path=/";
             })()
         },
         /*獲取本地存儲(chǔ)數(shù)據(jù)*/
         getLocalCookie: function (k) {
           k = k || "localDataTemp";
           return typeof window.localStorage !== "undefined" ? localStorage.getItem(k) :
             (function () {
               var all = document.cookie.split(";");
               var cookieData = {};
               for (var i = 0, l = all.length; i < l; i++) {
                 var p = all[i].indexOf("=");
                 var dataName = all[i].substring(0, p).replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");
                 cookieData[dataName] = all[i].substring(p + 1);
               }
               return cookieData[k]
             })();
         },
         /*刪除本地存儲(chǔ)數(shù)據(jù)*/
         clearLocalData: function (k) {
           k = k || "localDataTemp";
           typeof window.localStorage !== "undefined" ? localStorage.removeItem(k) :
             (function () {
               document.cookie = k + "=temp" + ";max-age=0";
             })()
         },
         init:function(){
           this.bindEvent();
         },
         //事件綁定
         bindEvent:function(){
           var _this=this;
           win.addEventListener("message",function(evt){
             if(win.parent!=evt.source){return}
             var options=JSON.parse(evt.data);
             if(options.type=="GET"){
               var data=tools.getLocalCookie(options.key);
               win.parent.postMessage(data, "*");
             }
             options.type=="SET"&&_this.setLocalCookie(options.key,options.value);
             options.type=="REM"&&_this.clearLocalData(options.key);
           },false)
         }
       };
       var tools=new fn();
       tools.init();
     })(document,window);
     /**CURD本地存儲(chǔ)信息 end**/

前端頁面js代碼(客戶端):

   //頁面初始化向iframe域名發(fā)送消息
    window.onload = function() {
      console.log('get key value......................')
      window.frames[0].postMessage(JSON.stringify({type:"GET",key:"User-Token"}),'*');
    }
    //監(jiān)聽message信息,接收從iframe域下獲取到的token信息,然后存儲(chǔ)到localstorage或cookie中
    window.addEventListener('message', function(e) {
      console.log('listen.....');
      var data = e.data;
      console.log(data);
      if(data != null){
        localStorage.setItem("User-Token", data);
      }
    }, false);

總結(jié):

優(yōu)點(diǎn):在非跨域環(huán)境下使用JWT機(jī)制是一個(gè)非常不錯(cuò)的選擇,實(shí)現(xiàn)方式簡(jiǎn)單,操作方便,能夠快速實(shí)現(xiàn)。由于服務(wù)端不存儲(chǔ)用戶狀態(tài)信息,因此大用戶量,對(duì)后臺(tái)服務(wù)也不會(huì)造成壓力;

缺點(diǎn):跨域?qū)崿F(xiàn)相對(duì)比較麻煩,安全性也有待探討。因?yàn)镴WT令牌返回到頁面中,可以使用js獲取到,如果遇到XSS攻擊令牌可能會(huì)被盜取,在JWT還沒超時(shí)的情況下,就會(huì)被獲取到敏感數(shù)據(jù)信息。

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

jwt
AI