日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

深入探討API網關APISIX中自定義Java插件在真實項目中的運用

來源: 責編: 時間:2024-01-18 17:40:18 290觀看
導讀環境:APISIX3.4.1 + JDK11 + SpringBoot2.7.12一. APISIX簡介APISIX 網關作為所有業務的流量入口,它提供了動態路由、動態上游、動態證書、A/B 測試、灰度發布(金絲雀發布)、藍綠部署、限速、防攻擊、收集指標、監控報警

環境:APISIX3.4.1 + JDK11 + SpringBoot2.7.12Xbx28資訊網——每日最新資訊28at.com

一. APISIX簡介

APISIX 網關作為所有業務的流量入口,它提供了動態路由、動態上游、動態證書、A/B 測試、灰度發布(金絲雀發布)、藍綠部署、限速、防攻擊、收集指標、監控報警、可觀測、服務治理等功能。Xbx28資訊網——每日最新資訊28at.com

為什么使用APISIX?Xbx28資訊網——每日最新資訊28at.com

  1. 高性能和可擴展性:APISIX是基于Nginx和OpenResty構建的,具有高性能和可擴展性。它支持動態路由、限流、緩存、認證等功能,并可以通過插件擴展其他功能。
  2. 社區活躍,易于使用:APISIX的社區非常活躍,提供了完整的文檔,使其易于使用。此外,它也支持類似于Kubernetes中的自動化部署,適合對網絡部署和管理要求高的團隊。
  3. 處理API和微服務流量的強大工具:Apache APISIX是一個動態、實時、高性能的開源API網關,可以快速、安全地處理API和微服務流量,包括網關、Kubernetes Ingress和服務網格等。全球已有數百家企業使用Apache APISIX處理關鍵業務流量,涵蓋金融、互聯網、制造、零售、運營商等各個領域。
  4. 云原生技術體系統一:APISIX從底層架構上避免了宕機、丟失數據等情況的發生。在控制面上,APISIX使用了etcd存儲配置信息,這與云原生技術體系更為統一,能更好地體現高可用特性。
  5. 實時配置更新:使用etcd作為存儲后,APISIX可以在毫秒級別內獲取到最新的配置信息,實現實時生效。相比之下,如果采用輪詢數據庫的方式,可能需要5-10秒才能獲取到最新的配置信息。

綜上所述,APISIX是一個優秀的開源API網關,具有高性能、可擴展性、社區活躍、易于使用等特點,并且能夠處理API和微服務流量,避免宕機、丟失數據等問題,實現實時配置更新。因此,許多企業和團隊選擇使用APISIX作為其API網關解決方案。Xbx28資訊網——每日最新資訊28at.com

二. APISIX安裝

關于APISIX的安裝參考官方文檔即可Xbx28資訊網——每日最新資訊28at.com


Xbx28資訊網——每日最新資訊28at.com

APISIX安裝指南Xbx28資訊網——每日最新資訊28at.com


Xbx28資訊網——每日最新資訊28at.com

https://apisix.apache.org/zh/docs/apisix/installation-guide/Xbx28資訊網——每日最新資訊28at.com

我使用的docker部署Xbx28資訊網——每日最新資訊28at.com

三. 需求說明

  1. 目的與背景:

由于安全需要,現有系統接口的請求數據需要加密(調用方必須加密傳輸)。Xbx28資訊網——每日最新資訊28at.com

考慮到對現有系統的最小化影響,決定采用APISIX作為中間件,通過自定義Java插件來實現數據加密的功能。Xbx28資訊網——每日最新資訊28at.com

  1. 功能需求:
  • 數據加密:插件需要能夠接收并解析請求數據,然后對數據進行解密處理(解密后的數據再提交到上游服務)。Xbx28資訊網——每日最新資訊28at.com

  • 安全性:加密算法和密鑰管理應遵循業界最佳實踐,確保數據安全。Xbx28資訊網——每日最新資訊28at.com

  • 錯誤處理與日志記錄:插件應具備良好的錯誤處理機制,并能夠記錄詳細的日志,以便于問題排查。(這通過記錄日志即可)Xbx28資訊網——每日最新資訊28at.com

  1. 非功能需求:Xbx28資訊網——每日最新資訊28at.com

  • 可維護性:插件代碼應清晰、模塊化,便于后續的維護和升級。Xbx28資訊網——每日最新資訊28at.com

  • 可擴展性:考慮到未來可能的加密需求變化,插件應具備良好的擴展性。Xbx28資訊網——每日最新資訊28at.com

四. 插件工作原理

apisix-java-plugin-runner 設計為使用 netty 構建的 TCP 服務器,它提供了一個 PluginFilter 接口供用戶實現。用戶只需關注其業務邏輯,而無需關注 apisix java 插件運行程序如何與 APISIX 通信的細節;它們之間的進程間通信如下圖所示。Xbx28資訊網——每日最新資訊28at.com

圖片圖片Xbx28資訊網——每日最新資訊28at.com

核心運行原理

官方的包是基于springboot,所以它自身提供了一個CommandLineRunner類,該類會在Springboot容器啟動完成后運行,也就是下面的地方執行:
Xbx28資訊網——每日最新資訊28at.com

public class SpringApplication {  public ConfigurableApplicationContext run(String... args) {    // ...這里ApplicationContext等相關的初始化    callRunners(context, applicationArguments);  }}

核心Runner類

public class ApplicationRunner implements CommandLineRunner {  private ObjectProvider<PluginFilter> filterProvider;  public void run(String... args) throws Exception {    if (socketFile.startsWith("unix:")) {      socketFile = socketFile.substring("unix:".length());    }    Path socketPath = Paths.get(socketFile);    Files.deleteIfExists(socketPath);    // 啟動netty服務    start(socketPath.toString());  }  public void start(String path) throws Exception {    EventLoopGroup group;    ServerBootstrap bootstrap = new ServerBootstrap();      // 判斷使用什么channel      bootstrap.group(group).channel(...)    try {      // 初始化netty服務      initServerBootstrap(bootstrap);      ChannelFuture future = bootstrap.bind(new DomainSocketAddress(path)).sync();      Runtime.getRuntime().exec("chmod 777 " + socketFile);      future.channel().closeFuture().sync();    } finally {      group.shutdownGracefully().sync();    }  }  private void initServerBootstrap(ServerBootstrap bootstrap) {    bootstrap.childHandler(new ChannelInitializer<DomainSocketChannel>() {      @Override      protected void initChannel(DomainSocketChannel channel) {        channel.pipeline().addFirst("logger", new LoggingHandler())          //...          // 核心Handler          .addAfter("payloadDecoder", "prepareConfHandler", createConfigReqHandler(cache, filterProvider, watcherProvider))          // ...      }    });  }}

五. 插件開發

5.1 依賴管理

<properties>  <java.version>11</java.version>  <spring-boot.version>2.7.12</spring-boot.version>  <apisix.version>0.4.0</apisix.version>  <keys.version>1.1.4</keys.version></properties><dependencies>  <dependency>    <groupId>org.apache.apisix</groupId>    <artifactId>apisix-runner-starter</artifactId>    <version>${apisix.version}</version>  </dependency>  <!-- 封裝了各類加解密功能如:SM,AES,RSA等算法-->  <dependency>    <groupId>com.pack.components</groupId>    <artifactId>pack-keys</artifactId>    <version>${keys.version}</version>  </dependency>  <dependency>    <groupId>com.fasterxml.jackson.core</groupId>    <artifactId>jackson-databind</artifactId>  </dependency>  <dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-web</artifactId>  </dependency></dependencies>

5.2 配置文件

這里的配置可有可無,都有默認值Xbx28資訊網——每日最新資訊28at.com

cache.config:  expired: ${APISIX_CONF_EXPIRE_TIME}  capacity: 1000socket:  file: ${APISIX_LISTEN_ADDRESS}

5.3 啟動類配置

@SpringBootApplication(scanBasePackages = { "com.pack", "org.apache.apisix.plugin.runner" })public class CryptoApisixPluginRunnerApplication {  public static void main(String[] args) {    new SpringApplicationBuilder(CryptoApisixPluginRunnerApplication.class).web(NONE).run(args);  }}

注意:關鍵就是上面的"org.apache.apisix.plugin.runner"包路徑。Xbx28資訊網——每日最新資訊28at.com

5.4 Filter開發

總共2個插件:Xbx28資訊網——每日最新資訊28at.com

  • java插件
    該插件用來對數據解密。
  • LUA插件
    該插件用來改寫請求body,因為在java插件中無法改寫。

定義一個抽象類,實現了通過的功能Xbx28資訊網——每日最新資訊28at.com

public abstract class AbstractDecryptPreFilter implements PluginFilter {  // 具體細節由子類實現  protected abstract void doFilterInternal(HttpRequest request, HttpResponse response, PluginFilterChain chain,     CryptModel cryptModel, CacheModel cache);  // 工具類專門用來讀取針對插件的配置信息  @Resource  protected ConfigProcessor<BaseCryptoModel> configCryptoProcessor;  // 工具類專門用來處理加解密  @Resource  protected CryptoProcessor cryptoProcessor;  // 工具類專門用來判斷路徑匹配  @Resource  protected PathProcessor pathProcessor ;  // 是否開啟了插件功能  protected boolean isEnabled(HttpRequest request, BaseCryptoModel cryptoModel) {    if (request == null || cryptoModel == null) {      return false;    }    return cryptoModel.isEnabled();  }  // 檢查請求,對有些請求是不進行處理的比如OPTIONS,HEADER。  protected boolean checkRequest(HttpRequest request, CryptModel cryptModel, CacheModel cache) {    if (isOptionsOrHeadOrTrace(request)) {      return false ;    }    String contentType = request.getHeader("content-type") ;    logger.info("request method: {}, content-type: {}", request.getMethod(), contentType) ;    if (isGetOrPostWithFormUrlEncoded(request, contentType)) {      Optional<Params> optionalParams = this.pathProcessor.queryParams(request, cryptModel.getParams()) ;      if (optionalParams.isPresent() && !optionalParams.get().getKeys().isEmpty()) {        cache.getParamNames().addAll(optionalParams.get().getKeys()) ;        return true  ;      }      return false ;    }    String body = request.getBody() ;    if (StringUtils.hasLength(body)) {      Body configBody = cryptModel.getBody();      if (this.pathProcessor.match(request, configBody.getExclude())) {        return false ;      }      if (configBody.getInclude().isEmpty()) {        return true ;      } else {        return this.pathProcessor.match(request, configBody.getInclude()) ;      }    }    return false ;  }    private boolean isOptionsOrHeadOrTrace(HttpRequest request) {      return request.getMethod() == Method.OPTIONS ||         request.getMethod() == Method.HEAD ||         request.getMethod() == Method.TRACE ;    }      private boolean isGetOrPostWithFormUrlEncoded(HttpRequest request, String contentType) {        return request.getMethod() == Method.GET ||                (request.getMethod() == Method.POST && PluginConfigConstants.X_WWW_FORM_URLENCODED.equalsIgnoreCase(contentType));    }  // PluginFilter的核心方法,內部實現都交給了子類實現doFilterInternal  @Override  public final void filter(HttpRequest request, HttpResponse response, PluginFilterChain chain) {    BaseCryptoModel cryptoModel = configCryptoProcessor.processor(request, this);    CryptModel model = null ;    if (cryptoModel instanceof CryptModel) {      model = (CryptModel) cryptoModel ;    }    logger.info("model: {}", model);    Assert.isNull(model, "錯誤的數據模型") ;    CacheModel cache = new CacheModel() ;    // 是否開啟了加解密插件功能 && 當前請求路徑是否與配置的路徑匹配,只有匹配的才進行處理    if (isEnabled(request, cryptoModel) && checkRequest(request, model, cache)) {      this.doFilterInternal(request, response, chain, model, cache);    }    chain.filter(request, response);  }  // 插件中是否需要請求正文  @Override  public Boolean requiredBody() {    return Boolean.TRUE;  }}

解密插件

@Component@Order(1)public class DecryptFilter extends AbstractDecryptPreFilter {  private static final Logger logger = LoggerFactory.getLogger(DecryptFilter.class) ;    @Override  public String name() {    return "Decrypt";  }  @Override  protected void doFilterInternal(HttpRequest request, HttpResponse response, PluginFilterChain chain,      CryptModel cryptModel, CacheModel cache    SecretFacade sf = this.cryptoProcessor.getSecretFacade(request, cryptModel) ;    String body = request.getBody() ;    if (StringUtils.hasLength(body)) {      logger.info("request uri: {}", request.getPath()) ;      // 解密請求body      String plainText = sf.decrypt(body);      request.setBody(plainText) ;      plainText = request.getBody() ;      // 下面設置是為了吧內容傳遞到lua腳本寫的插件中,因為在java插件中無法改寫請求body      request.setHeader(PluginConfigConstants.DECRYPT_DATA_PREFIX, Base64.getEncoder().encodeToString(plainText.getBytes(StandardCharsets.UTF_8))) ;      request.setHeader(PluginConfigConstants.X_O_E, "1") ;      // 必須設置,不然響應內容類型就成了text/plain      request.setHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) ;    }  }  @Override  public Boolean requiredBody() {    return Boolean.TRUE;  }}

LUA插件

local ngx = ngx;local core = require "apisix.core"local plugin_name = "modify-body"local process_java_plugin_decrypt_data = "p_j_p_decrypt_data_"local x_o_e_flag = "x-o-e-flag"local schema = {}local metadata_schema = {}local _M = {    version = 0.1,    priority = 10,    name = plugin_name,    schema = schema,    metadata_schema = metadata_schema,    run_policy = 'prefer_route',}function _M.check_schema(conf)    return core.schema.check(schema, conf)endfunction _M.access(conf, ctx)  -- local cjson = require 'cjson'  -- ngx.req.read_body()  -- local body = ngx.req.get_body_data()  -- ngx.log(ngx.STDERR, "access content: ", body)endfunction _M.rewrite(conf, ctx)  local params, err = ngx.req.get_headers() --ngx.req.get_uri_args()  local flag = params[x_o_e_flag]  ngx.log(ngx.STDERR, "processor body, flag: ", flag)  if flag and flag == '1' then     local plain_data = params[process_java_plugin_decrypt_data]    if plain_data then      local data = ngx.decode_base64(plain_data)      -- 清除附加請求header      ngx.req.set_header(process_java_plugin_decrypt_data, nil)      -- 重寫body數據      ngx.req.set_body_data(data)      -- 這里如果計算不準,最好不傳      ngx.req.set_header('Content-Length', nil)    end  endendfunction _M.body_filter(conf, ctx)endreturn _M ;

接下來就是將該項目打包成jar。Xbx28資訊網——每日最新資訊28at.com

以上就完成插件的開發,接下來就是配置Xbx28資訊網——每日最新資訊28at.com

5.5 插件配置

Java插件配置

將上一步打包后的jar長傳到服務器,在config.yaml中配置插件Xbx28資訊網——每日最新資訊28at.com

ext-plugin:  cmd: ['java', '-Dfile.encoding=UTF-8', '-jar', '/app/plugins/crypto-apisix-plugin-runner-1.0.0.jar']

LUA插件配置Xbx28資訊網——每日最新資訊28at.com

將lua腳本上傳到docker容器Xbx28資訊網——每日最新資訊28at.com

docker cp modify-body.lua apisix-java-apisix-1:/usr/local/apisix/apisix/plugins/modify-body.lua

配置該插件Xbx28資訊網——每日最新資訊28at.com

plugins:  - ext-plugin-pre-req  - ext-plugin-post-req  - ext-plugin-post-resp  - modify-body

要想在apisix-dashboard中能夠使用,需要導出schema.json文件Xbx28資訊網——每日最新資訊28at.com

docker exec -it apisix-java-apisix-1 curl http://localhost:9092/v1/schema > schema.json

上傳該schema.json到apisix-dashboard中Xbx28資訊網——每日最新資訊28at.com

docker cp schema.json apisix-java-apisix-dashboard-1:/usr/local/apisix-dashboard/conf

重啟相應服務Xbx28資訊網——每日最新資訊28at.com

docker restart apisix-java-apisix-dashboard-1docker restart apisix-java-apisix-1

完成以上步驟后,接下來就可以通過dashboard進行路徑配置了。Xbx28資訊網——每日最新資訊28at.com

六. 路由配置

這里直接貼插件的配置Xbx28資訊網——每日最新資訊28at.com

"plugins": {  "ext-plugin-pre-req": {    "allow_degradation": false,    "conf": [      {        "name": "Decrypt",        "value": "{/"enabled/": /"true/",/"apiKey/": /"kzV7HpPsZfTwJnZbyWbUJw==/", /"alg/": /"sm/", /"params/": [{/"pattern/": /"/api-1/**/", /"keys/": [/"idNo/"]}],/"body/": {/"exclude/": [/"/api-a/**/"],/"include/": [/"/api-1/**/"]}}"      }    ]  },  "modify-body": {},  "proxy-rewrite": {    "regex_uri": [      "^/api-1/(.*)$",      "/$1"    ]  }}

注意:modify-body插件一定要配置,這個是專門用來改寫請求body內容的。Xbx28資訊網——每日最新資訊28at.com

到此一個完整的插件就開發完成了,希望本篇文章能夠幫到你。如有需要,可提供其它代碼。Xbx28資訊網——每日最新資訊28at.com

完畢!!!Xbx28資訊網——每日最新資訊28at.com

本文鏈接:http://m.www897cc.com/showinfo-26-64508-0.html深入探討API網關APISIX中自定義Java插件在真實項目中的運用

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 針對大型數據庫,如何優化MySQL事務的性能?

下一篇: 使用 Spring Boot 創建自己的 ChatGPT 應用程序

標簽:
  • 熱門焦點
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
久久久999成人| 99riav1国产精品视频| 国产精品久久久久永久免费观看| 欧美四级剧情无删版影片| 国产精品国产三级国产aⅴ入口| 国产精品拍天天在线| 国产综合婷婷| 亚洲精品中文字幕女同| 亚洲一区日韩| 久久嫩草精品久久久精品| 欧美激情一区二区三区在线 | 欧美亚洲第一区| 国产伦精品一区二区| 一区二区亚洲| 一区二区不卡在线视频 午夜欧美不卡在 | 久久久久欧美精品| 欧美久久久久免费| 国产乱码精品一区二区三| 亚洲成人资源网| 亚洲一区图片| 蜜臀a∨国产成人精品| 国产精品第2页| 午夜精品久久久久久久蜜桃app| 欧美亚洲一区二区在线| 欧美黄色影院| 国产一区二区三区高清播放| 99re在线精品| 久久国产精品毛片| 欧美日在线观看| 亚洲电影免费观看高清完整版在线观看| 一区二区三区日韩| 麻豆精品网站| 国产区二精品视| 99伊人成综合| 玖玖综合伊人| 国产区在线观看成人精品| 亚洲国产欧美在线| 欧美一级电影久久| 欧美男人的天堂| 激情视频一区二区| 亚洲欧美日韩高清| 欧美日韩第一区| 亚洲第一精品久久忘忧草社区| 亚洲欧美在线播放| 欧美日韩一区二区视频在线 | 99riav国产精品| 老**午夜毛片一区二区三区| 国产精品一区二区欧美| 99re6这里只有精品| 另类激情亚洲| 国内外成人在线| 午夜精品区一区二区三| 欧美日韩一视频区二区| 91久久线看在观草草青青| 久久久久久久一区二区| 国产日韩亚洲欧美精品| 亚洲综合成人在线| 欧美视频在线观看视频极品| 亚洲精品一区二区三区蜜桃久| 亚洲国内欧美| 久久国产精品一区二区三区| 国产精品久久二区| 99天天综合性| 欧美精品激情blacked18| 1024亚洲| 麻豆视频一区二区| 狠狠88综合久久久久综合网| 欧美亚洲一级片| 国产精品亚洲成人| 亚洲女ⅴideoshd黑人| 欧美性大战久久久久久久| 99在线精品观看| 欧美全黄视频| 日韩亚洲视频在线| 欧美理论片在线观看| 亚洲精品一二三| 欧美美女操人视频| 日韩图片一区| 欧美日韩三区| 亚洲午夜精品福利| 国产精品久久午夜| 午夜精品久久久久久久男人的天堂| 国产精品久久久久久模特| 亚洲伊人网站| 国产精品亚洲视频| 午夜亚洲福利| 国产一区二区三区久久| 久久久久久久国产| 在线观看日韩一区| 欧美高清视频一区二区| 亚洲精品一区二区在线| 欧美日韩国产系列| 亚洲视频每日更新| 国产精品午夜电影| 久久精品久久综合| 在线免费精品视频| 欧美国产免费| 在线视频亚洲欧美| 国产精品网站在线播放| 久久精品国产视频| 亚洲成色精品| 欧美女主播在线| 亚洲一区二区免费在线| 国产精品一区二区视频| 久久久久国产成人精品亚洲午夜| 尤物网精品视频| 欧美福利网址| 亚洲午夜性刺激影院| 国产欧美一区二区精品仙草咪| 久久精品国产第一区二区三区最新章节| 一区二区在线观看av| 欧美精品v国产精品v日韩精品| 亚洲色图制服丝袜| 国产亚洲欧美一级| 久久天天躁狠狠躁夜夜爽蜜月| 亚洲精品国产精品国自产在线| 欧美午夜精品久久久久久久| 欧美在线播放一区二区| 亚洲电影有码| 国产精品vvv| 久久久精品五月天| 亚洲伦理在线| 国产日韩在线一区二区三区| 欧美gay视频| 亚洲免费中文字幕| …久久精品99久久香蕉国产| 亚洲午夜女主播在线直播| 国产精品午夜视频| 欧美1级日本1级| 亚洲视频一区二区| 精品动漫3d一区二区三区| 欧美日韩理论| 久久大逼视频| 日韩视频一区二区| 国产一区av在线| 欧美日韩免费在线| 久久精品一本| 亚洲少妇在线| 影音先锋在线一区| 国产精品白丝jk黑袜喷水| 久久伊人精品天天| 亚洲午夜久久久| 亚洲电影免费在线观看| 国产精品外国| 欧美日本中文| 久久亚洲国产精品一区二区| 亚洲网站在线观看| 亚洲国产免费| 国产欧美一区二区三区另类精品| 欧美成人精品在线观看| 性亚洲最疯狂xxxx高清| 日韩午夜精品视频| 一区在线播放| 国产精品综合久久久| 欧美日本国产视频| 久久综合久久久久88| 午夜精品偷拍| 一区二区精品| 亚洲国产欧美日韩| 韩国精品久久久999| 国产精品美女久久久久久久| 欧美激情影院| 狂野欧美一区| 欧美在线短视频| 亚洲一区3d动漫同人无遮挡| 91久久久亚洲精品| 娇妻被交换粗又大又硬视频欧美| 国产精品女同互慰在线看| 欧美激情一区二区三区四区| 久久欧美中文字幕| 欧美在现视频| 亚洲欧美在线高清| 亚洲一区二区在线看| 99精品国产高清一区二区| 在线观看欧美激情| 国产专区一区| 国产日韩欧美日韩| 国产精品日韩一区二区三区| 欧美日韩一区二区三区在线看| 欧美精品成人91久久久久久久| 麻豆九一精品爱看视频在线观看免费 | 一区二区日韩| 亚洲精品久久| 亚洲国产日韩欧美在线99| 狠狠做深爱婷婷久久综合一区| 国产欧美一区二区三区沐欲| 国产精品久久久久天堂| 欧美午夜电影一区| 欧美日韩免费观看一区三区 | 在线观看三级视频欧美| 激情久久综合| 黄色精品免费| 韩国一区二区三区在线观看| 国产一区在线观看视频| 国产一区二区三区久久悠悠色av| 国产手机视频精品| 国产三级欧美三级日产三级99| 国产精品欧美日韩久久| 国产精品女主播| 国产精品一区=区| 国产精品午夜视频| 国产乱码精品一区二区三区五月婷| 国产精品人人做人人爽人人添| 国产精品女主播一区二区三区|