{"id":32591246,"url":"https://github.com/linyuanbaobao/http-proxy-servlet","last_synced_at":"2025-10-30T01:56:47.490Z","repository":{"id":50108332,"uuid":"227114491","full_name":"LinYuanBaoBao/http-proxy-servlet","owner":"LinYuanBaoBao","description":"HTTP、HTTPS 请求代理 Servlet","archived":false,"fork":false,"pushed_at":"2021-06-04T02:23:14.000Z","size":47,"stargazers_count":8,"open_issues_count":4,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-06T23:04:26.426Z","etag":null,"topics":["http-proxy-servlet","java","proxy"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LinYuanBaoBao.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-12-10T12:24:54.000Z","updated_at":"2024-01-24T06:25:24.000Z","dependencies_parsed_at":"2022-09-10T20:22:05.110Z","dependency_job_id":null,"html_url":"https://github.com/LinYuanBaoBao/http-proxy-servlet","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/LinYuanBaoBao/http-proxy-servlet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LinYuanBaoBao%2Fhttp-proxy-servlet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LinYuanBaoBao%2Fhttp-proxy-servlet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LinYuanBaoBao%2Fhttp-proxy-servlet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LinYuanBaoBao%2Fhttp-proxy-servlet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LinYuanBaoBao","download_url":"https://codeload.github.com/LinYuanBaoBao/http-proxy-servlet/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LinYuanBaoBao%2Fhttp-proxy-servlet/sbom","scorecard":{"id":84428,"data":{"date":"2025-08-11","repo":{"name":"github.com/LinYuanBaoBao/http-proxy-servlet","commit":"b3497c8dd764ebf94b0cc45ff5042a137d3ed55e"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.3,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/8 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"72 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-vmq6-5m68-f53m","Warn: Project is vulnerable to: GHSA-668q-qrv7-99fm","Warn: Project is vulnerable to: GHSA-6v67-2wr5-gvf4","Warn: Project is vulnerable to: GHSA-pr98-23f8-jwxv","Warn: Project is vulnerable to: GHSA-h46c-h94j-95f3","Warn: Project is vulnerable to: GHSA-wf8f-6423-gfxg","Warn: Project is vulnerable to: GHSA-288c-cq4h-88gq","Warn: Project is vulnerable to: GHSA-3x8x-79m2-3w2w","Warn: Project is vulnerable to: GHSA-57j2-w4cx-62h2","Warn: Project is vulnerable to: GHSA-jjjh-jjxp-wpff","Warn: Project is vulnerable to: GHSA-rgv9-q543-rqg4","Warn: Project is vulnerable to: GHSA-j288-q9x7-2f5v","Warn: Project is vulnerable to: GHSA-7r82-7xv7-xcpj","Warn: Project is vulnerable to: GHSA-27hp-xhwr-wr2m","Warn: Project is vulnerable to: GHSA-2rvv-w9r2-rg7m","Warn: Project is vulnerable to: GHSA-5j33-cvvr-w245","Warn: Project is vulnerable to: GHSA-767j-jfh2-jvrc","Warn: Project is vulnerable to: GHSA-7w75-32cg-r6g2","Warn: Project is vulnerable to: GHSA-83qj-6fr2-vhqg","Warn: Project is vulnerable to: GHSA-9xcj-c8cr-8c3c","Warn: Project is vulnerable to: GHSA-c9hw-wf7x-jp9j","Warn: Project is vulnerable to: GHSA-fccv-jmmp-qg76","Warn: Project is vulnerable to: GHSA-g8pj-r55q-5c2v","Warn: Project is vulnerable to: GHSA-h2fw-rfh5-95r3","Warn: Project is vulnerable to: GHSA-h3gc-qfqq-6h8f","Warn: Project is vulnerable to: GHSA-hfrx-6qgj-fp6c","Warn: Project is vulnerable to: GHSA-j39c-c8hj-x4j3","Warn: Project is vulnerable to: GHSA-jgwr-3qm3-26f3","Warn: Project is vulnerable to: GHSA-p22x-g9px-3945","Warn: Project is vulnerable to: GHSA-q3mw-pvr8-9ggc","Warn: Project is vulnerable to: GHSA-qppj-fm5r-hxr3","Warn: Project is vulnerable to: GHSA-qxf4-chvg-4r8r","Warn: Project is vulnerable to: GHSA-r6j3-px5g-cq3x","Warn: Project is vulnerable to: GHSA-wc4r-xq3c-5cf3","Warn: Project is vulnerable to: GHSA-wm9w-rjj3-j356","Warn: Project is vulnerable to: GHSA-v682-8vv8-vpwr","Warn: Project is vulnerable to: GHSA-7v6m-28jr-rg84","Warn: Project is vulnerable to: GHSA-rmrm-75hp-phr2","Warn: Project is vulnerable to: GHSA-x83m-pf6f-pf9g","Warn: Project is vulnerable to: GHSA-cm59-pr5q-cw85","Warn: Project is vulnerable to: GHSA-rc42-6c7j-7h5r","Warn: Project is vulnerable to: GHSA-xf96-w227-r7c4","Warn: Project is vulnerable to: GHSA-36p3-wjmg-h94x","Warn: Project is vulnerable to: GHSA-hh26-6xwr-ggv7","Warn: Project is vulnerable to: GHSA-4gc7-5j7h-4qph","Warn: Project is vulnerable to: GHSA-4wp7-92pw-q264","Warn: Project is vulnerable to: GHSA-g5mm-vmx4-3rg7","Warn: Project is vulnerable to: GHSA-6gf2-pvqw-37ph","Warn: Project is vulnerable to: GHSA-rfmp-97jj-h8m6","Warn: Project is vulnerable to: GHSA-558x-2xjg-6232","Warn: Project is vulnerable to: GHSA-564r-hj7v-mcr5","Warn: Project is vulnerable to: GHSA-9cmq-m9j5-mvww","Warn: Project is vulnerable to: GHSA-wxqc-pxw9-g2p8","Warn: Project is vulnerable to: GHSA-2rmj-mq67-h97g","Warn: Project is vulnerable to: GHSA-2wrp-6fg6-hmc5","Warn: Project is vulnerable to: GHSA-4wrc-f8pq-fpqp","Warn: Project is vulnerable to: GHSA-ccgv-vj62-xf9h","Warn: Project is vulnerable to: GHSA-gfwj-fwqj-fp3v","Warn: Project is vulnerable to: GHSA-hgjh-9rj2-g67j","Warn: Project is vulnerable to: GHSA-7pm4-g2qj-j85x","Warn: Project is vulnerable to: GHSA-8wx2-9q48-vm9r","Warn: Project is vulnerable to: GHSA-g5vr-rgqm-vf78","Warn: Project is vulnerable to: GHSA-3mc7-4q67-w48m","Warn: Project is vulnerable to: GHSA-98wm-3w3q-mw94","Warn: Project is vulnerable to: GHSA-9w3m-gqgf-c4p9","Warn: Project is vulnerable to: GHSA-c4r9-r8fh-9vj2","Warn: Project is vulnerable to: GHSA-hhhw-99gj-p3c3","Warn: Project is vulnerable to: GHSA-mjmj-j48q-9wg2","Warn: Project is vulnerable to: GHSA-rvwf-54qp-4r6v","Warn: Project is vulnerable to: GHSA-w37g-rhq8-7m4j","Warn: Project is vulnerable to: GHSA-78wr-2p64-hpwj","Warn: Project is vulnerable to: GHSA-gwrp-pvrq-jmwv"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-15T06:36:44.705Z","repository_id":50108332,"created_at":"2025-08-15T06:36:44.705Z","updated_at":"2025-08-15T06:36:44.705Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281731412,"owners_count":26551803,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-29T02:00:06.901Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["http-proxy-servlet","java","proxy"],"created_at":"2025-10-30T01:56:35.693Z","updated_at":"2025-10-30T01:56:47.485Z","avatar_url":"https://github.com/LinYuanBaoBao.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Http-Proxy-Servlet\n\nHttp-Proxy-Servlet 能提供流量转发功能，并且支持 HTTPS 双向认证。它可以很方便的整合到你的 SpringBoot 程序中，甚至你可以在此基础上搭建一个\"网关\"程序。\n\n项目地址：https://github.com/LinYuanBaoBao/http-proxy-servlet.git\n\n## 快速上手\n\n引入依赖：\n```java\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.LinYuanBaoBao\u003c/groupId\u003e\n    \u003cartifactId\u003ehttp-proxy-servlet\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.2-RELEASE\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### 在 SpringBoot 中使用\n\n\u003e 本示例你可以从 `http-proxy-servlet-example-http` 中获取\n\n在 SpringBoot 中使用 Http-Proxy-Servlet 非常的简单，通过 SpringMVC 提供的 `ServletWrappingController` 将 Servlet 包装成一个 Controller，并通过 `SimpleUrlHandlerMapping` 将 Controller 与 `/proxy` 接口进行绑定。\n\n```java\n@Configuration\npublic class ProxyServletConfiguration {\n\n    private final String PROXY_CONTROLLER = \"proxyController\";\n    private final String PROXY_API = \"/proxy\";\n\n    @Bean\n    public ServletWrappingController proxyController() throws Exception {\n        // Servlet 配置\n        Properties properties = new Properties();\n        properties.put(ProxyServletConfig.P_CONTEXT, PROXY_API);\n        properties.put(ProxyServletConfig.P_LOG, true);\n        properties.put(ProxyServletConfig.P_READ_TIMEOUT, -1);\n        properties.put(ProxyServletConfig.P_CONNECTION_REQUEST_TIMEOUT, -1);\n        properties.put(ProxyServletConfig.P_CONNECT_TIMEOUT, -1);\n\n        ServletWrappingController controller = new ServletWrappingController();\n        controller.setServletClass(SpringProxyServlet.class);\n        controller.setBeanName(PROXY_CONTROLLER);\n        controller.setInitParameters(properties);\n        controller.afterPropertiesSet();\n        return controller;\n    }\n\n    @Bean\n    public SimpleUrlHandlerMapping proxyControllerMapping() {\n        SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();\n        Properties urlProperties = new Properties();\n        urlProperties.put(PROXY_API + \"/**\", PROXY_CONTROLLER);\n        mapping.setMappings(urlProperties);\n        mapping.setOrder(Integer.MAX_VALUE - 2);\n        return mapping;\n    }\n\n    public static class SpringProxyServlet extends ProxyServlet {\n        @Override\n        public void initHandler() {\n            addHandler(new HttpClientProxyInitHandler());\n            addHandler(new ProxyHandler());\n            addHandler(new ResponseHandler());\n        }\n    }\n\n}\n```\n\n因为 **ProxyServlet** 是个抽象类，需要实现 `initHandler()` 方法初始化处理器，上面代码中添加了三个 **Handler** 用于支持基本的 HTTP 流量转发功能：\n\n- **HttpClientProxyInitHandler**：前置处理器，用于初始化 HttpClientProxy 代理对象\n- **ProxyHandler**：代理处理器，用于执行代理请求\n- **ResponseHandler**：后置处理器，用于将代理请求结果响应回客户端\n\n你也可以直接使用 `DefaultProxyServlet`，其同样初始化了这三个处理器。\n\n#### 添加 TargetHandler 处理器\n\n单单如此还不足以实现流量转发功能，你还需要自定义一个前置处理器，用于告诉 `ProxyServlet` 你准备将把请求代理至何处。\n\n这需要你自定义一个 **TargetHandler** 并在业务逻辑中获取待目标的主机地址，如下面代码：\n\n```java\n@Component\npublic class TargetHandler implements Handler {\n\n    @Override\n    public void doHandle(RequestContext requestContext) {\n        // 将请求代理至 localhost:8081 \n        TargetHost targetHost = new TargetHost(\"localhost\",8081);\n        requestContext.setTargetHost(targetHost);\n    }\n\n    @Override\n    public HandlerType getType() {\n        return HandlerType.PRE_PROXY;\n    }\n\n    @Override\n    public Integer getOrder() {\n        return Integer.MIN_VALUE;\n    }\n\n}\n```\n\nHandler 类型有三种：\n\n- **PRE_PROXY**：前置处理器，它将会在请求代理前运行\n- **PROXY**：请求代理处理器\n- **POST_PROXY**：前置处理器，它将会在请求代理后运行\n\n`getOrder()` 方法返回值将会决定 Handler 在处理链中的执行顺序，**其值越小越先执行，值相同则会根据添加的先后顺序。**\n\n- 对于需要最先执行的 Handler 通常会使用 Integer.MIN_VALUE 值，如用于初始化 Proxy 代理对象的处理器。\n- 对于需要最后执行的 Handler 通常会使用 Integer.MAX_VALUE 值，如用于将代理请求结果响应会客户端的处理器。\n\n修改 `initHandler()` 方法，将 TargetHandler 添加至处理器链，如下：\n\n```java\npublic static class SpringProxyServlet extends ProxyServlet {\n    @Override\n    public void initHandler() {\n        addHandler(new TargetHandler());\n        addHandler(new HttpClientProxyInitHandler());\n        addHandler(new ProxyHandler());\n        addHandler(new ResponseHandler());\n    }\n}\n```\n\n#### 测试\n\n此时运行程序，假设访问代理服务 http://localhost:8080/proxy/v1/applications 的请求将会被代理至 http://localhost:8081/v1/applications \n\n### 支持 HTTPS 双向认证\n\n\u003e 本示例你可以从 `http-proxy-servlet-example-https` 中获取\n\n若代理的目标服务支持 Https，并且目标服务还需验证请求客户端的身份，那么你可以参考下面示例实现 Https 双向认证。\n\n首先对先前的 **TargetHandler** 做一些修改：\n\n```java\npublic class TargetHandler implements Handler {\n\n    @Override\n    public void doHandle(RequestContext requestContext) {\n        TargetHost targetHost = new TargetHost(\"localhost\", 8082);\n        requestContext.setTargetHost(targetHost);\n        requestContext.setScheme(Scheme.HTTPS);\n    }\n\n    @Override\n    public HandlerType getType() {\n        return HandlerType.PRE_PROXY;\n    }\n\n    @Override\n    public Integer getOrder() {\n        return Integer.MIN_VALUE;\n    }\n\n}\n```\n\n我们需要将请求 **Scheme** 类型设置为 HTTPS。\n\n其次确保 `getOrder()` 返回值为 Integer.MIN_VALUE，HttpsClientProxyInitHandler 会根据 **Scheme** 决定是否创建 HTTPS 连接，因此需要 **TargetHandler** 先与 **HttpsClientProxyInitHandler** 执行。\n\n修改 `initHandler()` 方法，将 **HttpClientProxyInitHandler** 替换为 **HttpsClientProxyInitHandler**。\n\n```java\npublic static class SpringProxyServlet extends ProxyServlet {\n  @Override\n  public void initHandler() {\n\n    addHandler(new TargetHandler());\n\n    // HTTPS 双向认证\n    KeyStoreLoader keyStoreLoader = new KeyStoreLoader() {\n      @Override\n      public KeyStoreModel loadKeyStoreModel(RequestContext requestContext) {\n        try {\n          KeyStore keyStore = KeyStore.getInstance(\"PKCS12\");\n          File certificateFile = ResourceUtils.getFile(\"classpath:client.p12\");\n          KeyStore.PasswordProtection password = new KeyStore.PasswordProtection(\"123456\".toCharArray());\n          keyStore.load(FileUtils.openInputStream(certificateFile), password.getPassword());\n          return new KeyStoreModel(keyStore, password);\n        } catch (Exception e) {\n          e.printStackTrace();\n        }\n        return null;\n      }\n    };\n    DefaultSSLContextInitializer sslContextInitializer = new DefaultSSLContextInitializer(keyStoreLoader);\n    DefaultHttpsConnectionSocketFactoryRegister httpsConnectionSocketFactoryRegister = new DefaultHttpsConnectionSocketFactoryRegister(sslContextInitializer);\n    addHandler(new HttpsClientProxyInitHandler(httpsConnectionSocketFactoryRegister));\n\n    addHandler(new ProxyHandler());\n    addHandler(new ResponseHandler());\n  }\n}\n```\n\n**HttpsClientProxyInitHandler** 构造函数需要一个 **HttpsConnectionSocketFactoryRegister** 接口实现，用于创建 HTTPS-Socket 连接工厂，这里使用 **DefaultHttpsConnectionSocketFactoryRegister**，需要注意的是它暂未对 *hostname* 进行验证，你可以自己实现验证逻辑，并调用 `setHostnameVerifier()` 方法配置它。\n\n而 **DefaultHttpsConnectionSocketFactoryRegister** 构造函数需要一个 **SSLContextInitializer** 接口实现，用于初始化 SSLContext 上下文对象，这里使用的 **DefaultSSLContextInitializer** 它会跳过对服务端的证书校验，你可以继承该类重写 `loadTrustManager()` 方法实现校验逻辑。\n\n此外 **DefaultSSLContextInitializer** 并不清楚你客户端的证书保存于何处，因为它可能跟你的业务逻辑有关，因此你需要实现 **KeyStoreLoader** 接口获取 **KeyStore**。\n\n上面代码中，我本地测试用的客户端证书位于项目资源目录下，并名为 client.p12，其密码为 123456，证书类型为 PKCS12。\n\n#### 测试\n\n此时如果代理的目标服务端已信任 client.p12 证书，则请求将会成功。\n\n你可以参考 [此文章](https://blog.csdn.net/BlackButton_CC/article/details/99956259) 搭建一个双向认证服务端尝试一下。\n\n### 自定义 Proxy 代理对象\n\n真正转发流量的并非是 **Handler** 而是 **Proxy**，由 **Proxy** 去完成实际代理并将结果响应回调用方。目前仅提供了基于 HttpClient 实现的 **HttpClientProxy**。\n\n如果有需要自定义你可以继承自 **AbstractProxy** 并实现以下三个方法：\n\n- **proxy()**：执行代理请求\n- **response()**：将代理请求结果响应回调用方\n- **destroy()**：销毁代理对象资源\n\n也可以继承 **HttpClientProxy** 并在原有的基础上做修改，如修改响应结果，可以重写 `setResponseEntity()` 方法。\n\n## 扩展参考\n\n### 通过跳板机将请求代理至内网主机\n\n如果你需要将请求代理至内网主机，你可以利用跳板机的端口转发功能，这里推荐使用 **Jcraft**，你的代码可能会是这个样子：\n\n```java\nprivate Integer buildTunnel(Session jumpSession, String targetIp, Integer targetPort) throws JSchException {\n  return jumpSession.setPortForwardingL(0, targetIp, targetPort);\n}\n```\n\n## 作者信息\n\n聪明的杰瑞 - [掘金博客](https://juejin.im/user/5bd1c4886fb9a05cd777874a)、邮箱：765371578@qq.com\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinyuanbaobao%2Fhttp-proxy-servlet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flinyuanbaobao%2Fhttp-proxy-servlet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinyuanbaobao%2Fhttp-proxy-servlet/lists"}