<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>

          快速搭建一個網(wǎng)關(guān)服務(wù),動態(tài)路由、鑒權(quán)的流程,看完秒會(含流程圖)

          共 17006字,需瀏覽 35分鐘

           ·

          2022-07-11 23:29

          前言

          本文記錄一下如何使用Gateway搭建網(wǎng)關(guān)服務(wù)及實現(xiàn)動態(tài)路由的,幫助大家學(xué)習(xí)如何快速搭建一個網(wǎng)關(guān)服務(wù),了解路由相關(guān)配置,鑒權(quán)的流程及業(yè)務(wù)處理,有興趣的一定看到最后,非常適合沒接觸過網(wǎng)關(guān)服務(wù)的同學(xué)當(dāng)作入門教程。

          搭建服務(wù)

          框架

          SpringBoot 2.1

          <parent>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-parent</artifactId>
             <version>2.1.0.RELEASE</version>
          </parent>

          Spring-cloud-gateway-core

          <dependency>
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-gateway-core</artifactId>
          </dependency>

          common-lang3

          <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
          </dependency>
          路由配置

          網(wǎng)關(guān)作為請求統(tǒng)一入口,路由就相當(dāng)于是每個業(yè)務(wù)系統(tǒng)的入口,通過路由規(guī)則則可以匹配到對應(yīng)微服務(wù)的入口,將請求命中到對應(yīng)的業(yè)務(wù)系統(tǒng)中

          server:
            port: 8080

          spring:
            cloud:
              gateway:
                enabled: true
                routes:
                - id: demo-server
                  uri: http://localhost:8081
                  predicates:
                  - Path=/demo-server/**
                  filters:
                    - StripPrefix= 1

          routes

          解讀配置
          • 現(xiàn)在有一個服務(wù)demo-server部署在本機,地址和端口為127.0.0.1:8081,所以路由配置uri為http://localhost:8081
          • 使用網(wǎng)關(guān)服務(wù)路由到此服務(wù),predicates -Path=/demo-server/**,網(wǎng)關(guān)服務(wù)的端口為8080,啟動網(wǎng)關(guān)服務(wù),訪問localhost:8080/demo-server,路由斷言就會將請求路由到demo-server
          • 直接訪問demo-server的接口localhost:8081/api/test,通過網(wǎng)關(guān)的訪問地址則為localhost:8080/demo-server/api/test,predicates配置將請求斷言到此路由,filters-StripPrefix=1代表將地址中/后的第一個截取,所以demo-server就截取掉了

          使用gateway通過配置文件即可完成路由的配置,非常方便,我們只要充分的了解配置項的含義及規(guī)則就可以了;但是這些配置如果要修改則需要重啟服務(wù),重啟網(wǎng)關(guān)服務(wù)會導(dǎo)致整個系統(tǒng)不可用,這一點是無法接受的,下面介紹如何通過Nacos實現(xiàn)動態(tài)路由

          動態(tài)路由

          使用nacos結(jié)合gateway-server實現(xiàn)動態(tài)路由,我們需要先部署一個nacos服務(wù),可以使用docker部署或下載源碼在本地啟動,具體操作可以參考官方文檔即可

          Nacos配置

          groupId: 使用網(wǎng)關(guān)服務(wù)名稱即可

          dataId: routes

          配置格式:json

          [{
                "id""xxx-server",
                "order": 1, #優(yōu)先級
                "predicates": [{ #路由斷言
                    "args": {
                        "pattern""/xxx-server/**"
                    },
                    "name""Path"
                }],
                "filters":[{ #過濾規(guī)則
                    "args": {
                        "parts": 0 #k8s服務(wù)內(nèi)部訪問容器為http://xxx-server/xxx-server的話,配置0即可
                    },
                    "name""StripPrefix" #截取的開始索引
                }],
                "uri""http://localhost:8080/xxx-server" #目標地址
            }]

          json格式配置項與yaml中對應(yīng),需要了解配置在json中的寫法

          關(guān)注公眾號:Java后端編程,回復(fù):666領(lǐng)取資料 。

          比對一下json配置與yaml配置
          {
              "id":"demo-server",
              "predicates":[
                  {
                      "args":{
                          "pattern":"/demo-server/**"
                      },
                      "name":"Path"
                  }
              ],
              "filters":[
                  {
                      "args":{
                          "parts":1
                      },
                      "name":"StripPrefix"
                  }
              ],
              "uri":"http://localhost:8081"
          }
          spring:
            cloud:
              gateway:
                enabled: true
                routes:
                - id: demo-server
                  uri: http://localhost:8081
                  predicates:
                  - Path=/demo-server/**
                  filters:
                    - StripPrefix= 1
          代碼實現(xiàn)

          Nacos實現(xiàn)動態(tài)路由的方式核心就是通過Nacos配置監(jiān)聽,配置發(fā)生改變后執(zhí)行網(wǎng)關(guān)相關(guān)api創(chuàng)建路由

          @Component
          public class NacosDynamicRouteService implements ApplicationEventPublisherAware {

              private static final Logger LOGGER = LoggerFactory.getLogger(NacosDynamicRouteService.class);

              @Autowired
              private RouteDefinitionWriter routeDefinitionWriter;

              private ApplicationEventPublisher applicationEventPublisher;

              /** 路由id */
              private static List<String> routeIds = Lists.newArrayList();

              /**
               * 監(jiān)聽nacos路由配置,動態(tài)改變路由
               * @param configInfo
               */

              @NacosConfigListener(dataId = "routes", groupId = "gateway-server")
              public void routeConfigListener(String configInfo) {
                  clearRoute();
                  try {
                      List<RouteDefinition> gatewayRouteDefinitions = JSON.parseArray(configInfo, RouteDefinition.class);
                      for (RouteDefinition routeDefinition : gatewayRouteDefinitions) {
                          addRoute(routeDefinition);
                      }
                      publish();
                      LOGGER.info("Dynamic Routing Publish Success");
                  } catch (Exception e) {
                      LOGGER.error(e.getMessage(), e);
                  }
                  
              }


              /**
               * 清空路由
               */

              private void clearRoute() {
                  for (String id : routeIds) {
                      routeDefinitionWriter.delete(Mono.just(id)).subscribe();
                  }
                  routeIds.clear();
              }

              @Override
              public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
                  this.applicationEventPublisher = applicationEventPublisher;
              }

              /**
               * 添加路由
               * 
               * @param definition
               */

              private void addRoute(RouteDefinition definition) {
                  try {
                      routeDefinitionWriter.save(Mono.just(definition)).subscribe();
                      routeIds.add(definition.getId());
                  } catch (Exception e) {
                      LOGGER.error(e.getMessage(), e);
                  }
              }

              /**
               * 發(fā)布路由、使路由生效
               */

              private void publish() {
                  this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
              }
          }
          過濾器

          gateway提供GlobalFilter及Ordered兩個接口用來定義過濾器,我們自定義過濾器只需要實現(xiàn)這個兩個接口即可

          • GlobalFilter filter() 實現(xiàn)過濾器業(yè)務(wù)
          • Ordered getOrder() 定義過濾器執(zhí)行順序

          通常一個網(wǎng)關(guān)服務(wù)的過濾主要包含 鑒權(quán)(是否登錄、是否黑名單、是否免登錄接口...) 限流(ip限流等等)功能,我們今天簡單介紹鑒權(quán)過濾器的流程實現(xiàn)

          鑒權(quán)過濾器

          需要實現(xiàn)鑒權(quán)過濾器,我們先得了解登錄及鑒權(quán)流程,如下圖所示

          由圖可知,我們鑒權(quán)過濾核心就是驗證token是否有效,所以我們網(wǎng)關(guān)服務(wù)需要與業(yè)務(wù)系統(tǒng)在同一個redis庫,先給網(wǎng)關(guān)添加redis依賴及配置

          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
          </dependency>
          spring:
            redis:
              host: redis-server
              port: 6379
              password:
              database: 0
          代碼實現(xiàn)
          1. 定義過濾器AuthFilter
          2. 獲取請求對象 從請求頭或參數(shù)或cookie中獲取token(支持多種方式傳token對于客戶端更加友好,比如部分web下載請求會新建一個頁面,在請求頭中傳token處理起來比較麻煩)
          3. 沒有token,返回401
          4. 有token,查詢redis是否有效
          5. 無效則返回401,有效則完成驗證放行
          6. 重置token過期時間、添加內(nèi)部請求頭信息方便業(yè)務(wù)系統(tǒng)權(quán)限處理
          @Component
          public class AuthFilter implements GlobalFilterOrdered {

              @Autowired
              private RedisTemplate<String, String> redisTemplate;

              private static final String TOKEN_HEADER_KEY = "auth_token";

              @Override
              public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                  // 1.獲取請求對象
                  ServerHttpRequest request = exchange.getRequest();
                  // 2.獲取token
                  String token = getToken(request);
                  ServerHttpResponse response = exchange.getResponse();
                  if (StringUtils.isBlank(token)) {
                      // 3.token為空 返回401
                      response.setStatusCode(HttpStatus.UNAUTHORIZED);
                      return response.setComplete();
                  }
                  // 4.驗證token是否有效
                  String userId = getUserIdByToken(token);
                  if (StringUtils.isBlank(userId)) {
                      // 5.token無效 返回401
                      response.setStatusCode(HttpStatus.UNAUTHORIZED);
                      return response.setComplete();
                  }
                  // token有效,后續(xù)業(yè)務(wù)處理
                  // 從寫請求頭,方便業(yè)務(wù)系統(tǒng)從請求頭獲取用戶id進行權(quán)限相關(guān)處理
                  ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
                  request = builder.header("user_id", userId).build();
                  // 延長緩存過期時間-token緩存用戶如果一直在操作就會一直重置過期
                  // 這樣避免用戶操作過程中突然過期影響業(yè)務(wù)操作及體驗,只有用戶操作間隔時間大于緩存過期時間才會過期
                  resetTokenExpirationTime(token, userId);
                  // 完成驗證
                  return chain.filter(exchange);
              }


              @Override
              public int getOrder() {
                  // 優(yōu)先級 越小越優(yōu)先
                  return 0;
              }

              /**
               * 從redis中獲取用戶id
               * 在登錄操作時候 登陸成功會生成一個token, redis得key為auth_token:token 值為用戶id
               *
               * @param token
               * @return
               */

              private String getUserIdByToken(String token) {
                  String redisKey = String.join(":""auth_token", token);
                  return redisTemplate.opsForValue().get(redisKey);
              }

              /**
               * 重置token過期時間
               *
               * @param token
               * @param userId
               */

              private void resetTokenExpirationTime(String token, String userId) {
                  String redisKey = String.join(":""auth_token", token);
                  redisTemplate.opsForValue().set(redisKey, userId, 2, TimeUnit.HOURS);
              }


              /**
               * 獲取token
               *
               * @param request
               * @return
               */

              private static String getToken(ServerHttpRequest request) {
                  HttpHeaders headers = request.getHeaders();
                  // 從請求頭獲取token
                  String token = headers.getFirst(TOKEN_HEADER_KEY);
                  if (StringUtils.isBlank(token)) {
                      // 請求頭無token則從url獲取token
                      token = request.getQueryParams().getFirst(TOKEN_HEADER_KEY);
                  }
                  if (StringUtils.isBlank(token)) {
                      // 請求頭和url都沒有token則從cookies獲取
                      HttpCookie cookie = request.getCookies().getFirst(TOKEN_HEADER_KEY);
                      if (cookie != null) {
                          token = cookie.getValue();
                      }
                  }
                  return token;
              }
          }

          總結(jié)

          Gateway通過配置項可以實現(xiàn)路由功能,整合Nacos及配置監(jiān)聽可以實現(xiàn)動態(tài)路由,實現(xiàn)GlobalFilter, Ordered兩個接口可以快速實現(xiàn)一個過濾器,文中也詳細的介紹了登錄后的請求鑒權(quán)流程,如果有不清楚地方可以評論區(qū)見咯。

          感謝閱讀,希望對你有所幫助 :) 

          來源:juejin.cn/post/7004756545741258765

          推薦閱讀:

          世界的真實格局分析,地球人類社會底層運行原理

          不是你需要中臺,而是一名合格的架構(gòu)師(附各大廠中臺建設(shè)PPT)

          企業(yè)IT技術(shù)架構(gòu)規(guī)劃方案

          論數(shù)字化轉(zhuǎn)型——轉(zhuǎn)什么,如何轉(zhuǎn)?

          華為干部與人才發(fā)展手冊(附PPT)

          企業(yè)10大管理流程圖,數(shù)字化轉(zhuǎn)型從業(yè)者必備!

          【中臺實踐】華為大數(shù)據(jù)中臺架構(gòu)分享.pdf

          華為的數(shù)字化轉(zhuǎn)型方法論

          華為如何實施數(shù)字化轉(zhuǎn)型(附PPT)

          超詳細280頁Docker實戰(zhàn)文檔!開放下載

          華為大數(shù)據(jù)解決方案(PPT)


          瀏覽 110
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  91久久久成人视频免费 | 日本aⅴ | 人人操黄色片段 | 苍井空免费一级A片 | 狠狠狠狠狠艹 |