<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      JWT單點登錄

      單點登錄



      概念:登錄某集團的某一產品之后,訪問其他產品的網站時就會是登錄狀態,比如登錄QQ之后,進入QQ游戲的時候就是登錄過的狀態,具體實現方法有以下:

      Redis+token實現單點登錄:

      生成一個隨機字符串token,以token為key,用戶信息為value存儲在redis緩存中,用戶訪問時帶著這個token就可以實現單點登錄;

      這里的token是無實際意義的,和用戶信息無關的,否則用戶每次登錄的時候token都是同一個,容易被黑;

      JWT實現單點登錄:

      這里jwt維護的也是一個token,但是這個token是有意義的,可以理解成用戶信息的加密數據。通過這串加密數據就可以反向解出當前登錄的是哪一個用戶。

      Redis+Token登錄和校驗流程:

      登錄->校驗用戶名密碼->生成隨機token->將token放入redis中并返回給前端->結束

      校驗->后端攔截請求,從header中獲取token,如果沒有就返回錯誤->根據token在redis中獲取數據->如果有數據校驗成功登錄,否則登錄校驗失敗->結束

      JWT單點登錄流程

      登錄->校驗用戶名密碼->JWT工具包隨機生成token->將token放入redis(也可以不放),并返回給前端->結束

      校驗->后端攔截請求,獲取header中的token,如果沒有就返回錯誤->使用工具包解密校驗token->如果校驗成功登錄,否則登錄校驗失敗->結束

      JWT原理

      Hutool參考文檔

      結構

      • Header 頭部信息,主要聲明了JWT的簽名算法等信息
      • Payload 載荷信息,主要承載了各種聲明并傳遞明文數據
      • Signature 簽名,擁有該部分的JWT被稱為JWS,也就是簽了名的JWS,用于校驗數據

      整體結構是:

      header.payload.signature
      

      使用

      JWT模塊的核心主要是兩個類:

      1. JWT類用于鏈式生成、解析或驗證JWT信息。
      2. JWTUtil類主要是JWT的一些工具封裝,提供更加簡潔的JWT生成、解析和驗證工作

      JWT生成

      1. HS265(HmacSHA256)算法

        // 密鑰

      點擊查看代碼
            // 密鑰
            byte[] key = "1234567890".getBytes();
            
            String token = JWT.create()
                .setPayload("sub", "1234567890")
                .setPayload("name", "looly")
                .setPayload("admin", true)
                .setKey(key)
                .sign();
      
        byte[] key = "1234567890".getBytes();
        
        String token = JWT.create()
            .setPayload("sub", "1234567890")
            .setPayload("name", "looly")
            .setPayload("admin", true)
            .setKey(key)
            .sign();
      

      生成的內容為:

      eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9.536690902d931d857d2f47d337ec81048ee09a8e71866bcc8404edbbcbf4cc40
      
      1. 其他算法
      點擊查看代碼
        
            // 密鑰
            byte[] key = "1234567890".getBytes();
            
            // SHA256withRSA
            String id = "rs256";
            JWTSigner signer = JWTSignerUtil.createSigner(id, 
                // 隨機生成密鑰對,此處用戶可自行讀取`KeyPair`、公鑰或私鑰生成`JWTSigner`
                KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
            
            String token = JWT.create()
                .setPayload("sub", "1234567890")
                .setPayload("name", "looly")
                .setPayload("admin", true)
                .setSigner(signer)
                .sign();
      
        // 密鑰
        byte[] key = "1234567890".getBytes();
        
        // SHA256withRSA
        String id = "rs256";
        JWTSigner signer = JWTSignerUtil.createSigner(id, 
            // 隨機生成密鑰對,此處用戶可自行讀取`KeyPair`、公鑰或私鑰生成`JWTSigner`
            KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
        
        String token = JWT.create()
            .setPayload("sub", "1234567890")
            .setPayload("name", "looly")
            .setPayload("admin", true)
            .setSigner(signer)
            .sign();
      
      1. 不簽名JWT
      點擊查看代碼
        
            //eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9.
            String token = JWT.create()
                .setPayload("sub", "1234567890")
                .setPayload("name", "looly")
                .setPayload("admin", true)
                .setSigner(JWTSignerUtil.none())
                .sign()
        
      
        //eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9.
        String token = JWT.create()
            .setPayload("sub", "1234567890")
            .setPayload("name", "looly")
            .setPayload("admin", true)
            .setSigner(JWTSignerUtil.none())
            .sign()
      

      JWT解析

      JWT驗證

      1. 驗證簽名
      點擊查看代碼
        
            String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." +
                "eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9." +
                "536690902d931d857d2f47d337ec81048ee09a8e71866bcc8404edbbcbf4cc40";
            
            // 密鑰
            byte[] key = "1234567890".getBytes();
            
            // 默認驗證HS265的算法
            JWT.of(rightToken).setKey(key).verify()
        
      
        String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." +
            "eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9." +
            "536690902d931d857d2f47d337ec81048ee09a8e71866bcc8404edbbcbf4cc40";
        
        // 密鑰
        byte[] key = "1234567890".getBytes();
        
        // 默認驗證HS265的算法
        JWT.of(rightToken).setKey(key).verify()
      
      1. 詳細驗證
      點擊查看代碼
      
          String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." +
              "eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9." +
              "536690902d931d857d2f47d337ec81048ee09a8e71866bcc8404edbbcbf4cc40";
          
          JWT jwt = JWT.of(rightToken);
          
          // JWT
          jwt.getHeader(JWTHeader.TYPE);
          // HS256
          jwt.getHeader(JWTHeader.ALGORITHM);
          
          // 1234567890
          jwt.getPayload("sub");
          // looly
          jwt.getPayload("name");
          // true
          jwt.getPayload("admin");
      

      除了驗證簽名,Hutool提供了更加詳細的驗證:validate,主要包括:

      • Token是否正確
      • 生效時間不能晚于當前時間
      • 失效時間不能早于當前時間
      • 簽發時間不能晚于當前時間

      使用方式如下:

      String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJNb0xpIiwiZXhwIjoxNjI0OTU4MDk0NTI4LCJpYXQiOjE2MjQ5NTgwMzQ1MjAsInVzZXIiOiJ1c2VyIn0.L0uB38p9sZrivbmP0VlDe--j_11YUXTu3TfHhfQhRKc";
      
      byte[] key = "1234567890".getBytes();
      boolean validate = JWT.of(token).setKey(key).validate(0);
      

      其他自定義詳細驗證見JWT驗證-JWTValidator章節。

      JWT存在的問題及解決方案講解

      1、token被解密

      加鹽值(密鑰),每個項目的鹽值不能一樣

      2、token被拿到第三方使用(別人包裝你的頁面使多個用戶的操作走一個用戶)

      沒啥好方法,使用限流

      包裝成工具類

      點擊查看代碼
      import cn.hutool.core.date.DateField;
      import cn.hutool.core.date.DateTime;
      import cn.hutool.json.JSONObject;
      import cn.hutool.jwt.JWT;
      import cn.hutool.jwt.JWTPayload;
      import cn.hutool.jwt.JWTUtil;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      
      import java.util.HashMap;
      import java.util.Map;
      
      public class JwtUtil {
          private static final Logger LOG = LoggerFactory.getLogger(JwtUtil.class);
      
          /**
           * 鹽值很重要,不能泄漏,且每個項目都應該不一樣,可以放到配置文件中
           */
          private static final String key = "Yubaibai12306";
      
          /**
           * 生成token
           * @param id
           * @param mobile
           * @return token
           */
          public static String createToken(Long id, String mobile) {
              DateTime now = DateTime.now();
              DateTime expTime = now.offsetNew(DateField.HOUR, 24);
              Map<String, Object> payload = new HashMap<>();
              // 簽發時間
              payload.put(JWTPayload.ISSUED_AT, now);
              // 過期時間
              payload.put(JWTPayload.EXPIRES_AT, expTime);
              // 生效時間
              payload.put(JWTPayload.NOT_BEFORE, now);
              // 內容
              payload.put("id", id);
              payload.put("mobile", mobile);
              String token = JWTUtil.createToken(payload, key.getBytes());
              LOG.info("生成JWT token:{}", token);
              return token;
          }
      
          /**
           * 驗證token
           * @param token
           * @return 校驗結果
           */
          public static boolean validate(String token) {
              JWT jwt = JWTUtil.parseToken(token).setKey(key.getBytes());
              // validate包含了verify
              boolean validate = jwt.validate(0);
              LOG.info("JWT token校驗結果:{}", validate);
              return validate;
          }
      
          /**
           * 解密token對應的內容
           * @param token
           * @return token對應內容對象
           */
          public static JSONObject getJSONObject(String token) {
              JWT jwt = JWTUtil.parseToken(token).setKey(key.getBytes());
              JSONObject payloads = jwt.getPayloads();
              payloads.remove(JWTPayload.ISSUED_AT);
              payloads.remove(JWTPayload.EXPIRES_AT);
              payloads.remove(JWTPayload.NOT_BEFORE);
              LOG.info("根據token獲取原始內容:{}", payloads);
              return payloads;
          }
      
          public static void main(String[] args) {
              createToken(1L, "123");
      
              String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYmYiOjE3MjUyNjQ1MzMsIm1vYmlsZSI6IjEyMyIsImlkIjoxLCJleHAiOjE3MjUzNTA5MzMsImlhdCI6MTcyNTI2NDUzM30.lSeDNb5QAp_CH1A-nF5Xw5Qk2zyvd4L3KrDmw97gQvM";
              validate(token);
      
              getJSONObject(token);
          }
      }
      
      

      前端使用vuex保存登錄信息

      vuex或稱store,用于存儲全局變量,可用于各頁面傳遞參數,或放置項目全局信息

      state:定義一個全局變量
      getters:獲取變量時,做些額外的轉換,如日期格式化
      mutations:相當于java的setter,用于修改變量
      actions:發起異步任務
      modules:項目較大,變量較多時,可以模塊化

      缺點:頁面刷新后,數據會丟失
      vuex配合h5的session解決瀏覽器刷新問題

      前端小技巧,使用:|| {} 為變量賦值,可防止空指針異常

      演示gateway攔截器的使用

      登錄校驗兩個步驟:
      前端請求帶上token,放在header里
      后端校驗token有效性,在gateway里統一校驗
      gateway有多個攔截器時,使用order來確定攔截器的順序

      點擊查看代碼
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.cloud.gateway.filter.GatewayFilterChain;
      import org.springframework.cloud.gateway.filter.GlobalFilter;
      import org.springframework.core.Ordered;
      import org.springframework.stereotype.Component;
      import org.springframework.web.server.ServerWebExchange;
      import reactor.core.publisher.Mono;
      
      @Component
      public class Test1Filter implements GlobalFilter, Ordered {
          private static final Logger LOG = LoggerFactory.getLogger(Test1Filter.class);
          @Override
          public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
              LOG.info("Test1Filter");
              return chain.filter(exchange);
      //        return exchange.getResponse().setComplete();
          }
      
          @Override
          public int getOrder() {
              return 1;
          }
      }
      
      點擊查看代碼
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.cloud.gateway.filter.GatewayFilterChain;
      import org.springframework.cloud.gateway.filter.GlobalFilter;
      import org.springframework.core.Ordered;
      import org.springframework.stereotype.Component;
      import org.springframework.web.server.ServerWebExchange;
      import reactor.core.publisher.Mono;
      
      @Component
      public class Test2Filter implements GlobalFilter, Ordered {
          private static final Logger LOG = LoggerFactory.getLogger(Test2Filter.class);
          @Override
          public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
              LOG.info("Test2Filter");
              return chain.filter(exchange);
      //        return exchange.getResponse().setComplete();
          }
      
          @Override
          public int getOrder() {
              return 0;
          }
      }
      

      測試結果:

      結果
      33:11.638 INFO  c.g.t.g.config.Test2Filter    :17   reactor-http-nio-2                    Test2Filter
      33:11.638 INFO  c.g.t.g.config.Test1Filter    :17   reactor-http-nio-2                    Test1Filter
      

      為gateway增加登錄校驗攔截器

      登錄攔截器
      import com.guaigen.train.gateway.util.JwtUtil;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.cloud.gateway.filter.GatewayFilterChain;
      import org.springframework.cloud.gateway.filter.GlobalFilter;
      import org.springframework.core.Ordered;
      import org.springframework.http.HttpStatus;
      import org.springframework.stereotype.Component;
      import org.springframework.web.server.ServerWebExchange;
      import reactor.core.publisher.Mono;
      
      @Component
      public class LoginMemberFilter implements GlobalFilter, Ordered {
          private static final Logger LOG = LoggerFactory.getLogger(LoginMemberFilter.class);
      
          @Override
          public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
              String path = exchange.getRequest().getURI().getPath();
      
              if (path.contains("/admin")
                  || path.contains("/hello")
                  || path.contains("/member/member/login")
                  || path.contains("/member/member/sendCode")) {
                  LOG.info("不需要登錄驗證:{}", path);
                  return chain.filter(exchange);
              }
      
              String token = exchange.getRequest().getHeaders().getFirst("token");
              LOG.info("會員登錄驗證開始, token:{}", token);
              if (token == null || token.isEmpty()) {
                  LOG.info("token為空請求被攔截");
                  exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                  return exchange.getResponse().setComplete();
              }
      
              // 驗證token是否有效
              boolean validate = JwtUtil.validate(token);
              if (validate) {
                  LOG.info("token有效,請求放行");
                  return chain.filter(exchange);
              } else {
                  LOG.info("token無效,請求攔截");
                  exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                  return exchange.getResponse().setComplete();
              }
          }
      
          @Override
          public int getOrder() {
              return 0;
          }
      }
      
      posted @ 2024-09-03 14:26  魚擺擺不擺  閱讀(157)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 成人嫩草研究院久久久精品| 天堂mv在线mv免费mv香蕉| 国产+亚洲+制服| 亚洲精品香蕉一区二区| 国产精品爽爽久久久久久竹菊| 国产精品一区二区久久岳| 中文日产幕无线码一区中文| 久99久热这里只有精品| 少妇又紧又色又爽又刺激视频| 国产又色又爽又黄的视频在线| 久久九九久精品国产免费直播| 免费av网站| 人妻有码av中文字幕久久琪| 国产成人精品亚洲精品日日| 免费中文熟妇在线影片| 久久久久四虎精品免费入口| 国产一区二区四区不卡| 国产精品中文一区二区| 国产真人无遮挡免费视频| 喀喇沁旗| 亚洲成人av综合一区| 国产亚洲精品在av| 日本黄韩国色三级三级三| 精品一区二区免费不卡| 色噜噜亚洲男人的天堂| 邻水| 国产又爽又黄又爽又刺激| 日本一道高清一区二区三区| 免费无遮挡无码视频网站| 噜噜综合亚洲av中文无码| 巴里| 日本肉体xxxx裸交| 综合激情亚洲丁香社区| 亚洲第一精品一二三区| 精品国产综合成人亚洲区| 亚洲欧美色一区二区三区| 99久久亚洲综合精品成人网| 99久久精品久久久久久婷婷| 无码国产精品一区二区VR老人| 精品国产成人国产在线观看| 人人妻人人澡人人爽人人精品av|