├── LICENSE ├── README.md ├── pom.xml └── src └── main ├── java └── com │ └── demo │ └── http │ └── api │ └── gateway │ ├── access │ ├── controller │ │ ├── ApiAuthFailedPastController.java │ │ ├── ApiBackendExceptionPastController.java │ │ └── ApiProxyController.java │ └── filter │ │ ├── RateLimitFilter.java │ │ └── RequestAuthFilter.java │ ├── constant │ ├── Constant.java │ └── ErrorEnum.java │ ├── dao │ └── mapper │ │ └── AppInfoMapper.java │ ├── main │ └── RestApiGateWayApplication.java │ ├── model │ ├── AppInfo.java │ └── ResponseModel.java │ ├── service │ └── AppInfoProvider.java │ ├── spring │ └── config │ │ └── DataSourceConfig.java │ └── util │ └── WebfluxForwardingUtil.java └── resources ├── application-dev.properties ├── application-pro.properties ├── application-uat.properties └── application.properties /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 1.http/rest api网关:作为反向代理,对外部系统提供统一的服务访问入口,进行鉴权、授权、限流等访问控制,通过后将请求转发给后端服务。 2 | 3 | 2.高性能:反应式&异步非阻塞io,springboot2+spring5 webflux包括webclient、webfilter、controller 4 | 5 | 3.详细介绍见[基于spring webflux的高性能rest api网关](http://wanshi.iteye.com/blog/2410210) -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.demo 6 | http.api.gateway 7 | 1.0.0 8 | jar 9 | 10 | http.api.gateway 11 | http://maven.apache.org 12 | 13 | 14 | org.springframework.boot 15 | spring-boot-starter-parent 16 | 2.0.0.RELEASE 17 | 18 | 19 | 20 | UTF-8 21 | UTF-8 22 | 1.8 23 | 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-webflux 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-test 34 | test 35 | 36 | 37 | 38 | io.projectreactor.addons 39 | reactor-test 40 | 3.0.3.RELEASE 41 | test 42 | 43 | 44 | 45 | commons-codec 46 | commons-codec 47 | 48 | 49 | com.github.ben-manes.caffeine 50 | caffeine 51 | 52 | 53 | org.mybatis.spring.boot 54 | mybatis-spring-boot-starter 55 | 1.3.1 56 | 57 | 58 | com.mchange 59 | c3p0 60 | 0.9.5.2 61 | 62 | 63 | 64 | mysql 65 | mysql-connector-java 66 | 6.0.6 67 | 68 | 74 | 75 | 76 | 77 | 78 | 79 | org.springframework.boot 80 | spring-boot-maven-plugin 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /src/main/java/com/demo/http/api/gateway/access/controller/ApiAuthFailedPastController.java: -------------------------------------------------------------------------------- 1 | package com.demo.http.api.gateway.access.controller; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | import org.springframework.web.server.ServerWebExchange; 7 | import com.demo.http.api.gateway.constant.Constant; 8 | import com.demo.http.api.gateway.constant.ErrorEnum; 9 | import com.demo.http.api.gateway.model.ResponseModel; 10 | import reactor.core.publisher.Mono; 11 | 12 | 13 | /** 14 | * 认证失败后控制器 15 | * 16 | * @author wang guobo 王国波 17 | */ 18 | @RestController 19 | public class ApiAuthFailedPastController { 20 | 21 | @RequestMapping(Constant.AUTH_FAILED_PATH) 22 | public Mono processIllegalRequest(ServerWebExchange exchange) 23 | { 24 | ErrorEnum error = (ErrorEnum)exchange.getAttributes().get(Constant.AUTH_ERROR_ATTR_NAME); 25 | //根据error的枚举值,设置不同的http status Code,如400 Bad Request,401 Unauthorized 26 | //这里只是演示,统一设置为401 Unauthorized 27 | exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); 28 | return Mono.just(new ResponseModel(error)); 29 | } 30 | 31 | /* 32 | @RequestMapping(Constant.AUTH_FAILED_PATH) 33 | public Mono processIllegalRequest(ServerWebExchange exchange) 34 | { 35 | ErrorEnum error = (ErrorEnum)exchange.getAttributes().get(Constant.AUTH_ERROR_ATTR_NAME); 36 | //根据error的枚举值,设置不同的http status Code,如400 Bad Request,401 Unauthorized 37 | //这里只是演示,统一设置为401 Unauthorized 38 | exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); 39 | return Mono.empty(); 40 | } 41 | */ 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/demo/http/api/gateway/access/controller/ApiBackendExceptionPastController.java: -------------------------------------------------------------------------------- 1 | package com.demo.http.api.gateway.access.controller; 2 | 3 | import java.net.ConnectException; 4 | import java.net.SocketException; 5 | import java.util.concurrent.TimeoutException; 6 | 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | import org.springframework.web.reactive.function.client.WebClientResponseException; 10 | import org.springframework.web.server.ServerWebExchange; 11 | 12 | import com.demo.http.api.gateway.constant.Constant; 13 | import com.demo.http.api.gateway.constant.ErrorEnum; 14 | import com.demo.http.api.gateway.model.ResponseModel; 15 | 16 | import reactor.core.publisher.Mono; 17 | 18 | /** 19 | * 处理后端服务异常(网关调用后端服务,后端服务发生异常)的后控制器,产生fallback响应 20 | * 21 | * @author wang guobo 王国波 22 | */ 23 | @RestController 24 | public class ApiBackendExceptionPastController { 25 | 26 | @RequestMapping(Constant.BACKEND_EXCEPTION_PATH) 27 | public Mono processExceptionRequest(ServerWebExchange exchange) 28 | { 29 | Throwable ex = (Throwable)exchange.getAttributes().get(Constant.BACKEND_EXCEPTION_ATTR_NAME); 30 | if(ex instanceof ConnectException) { 31 | return Mono.just(new ResponseModel(ErrorEnum.BACKEND_CONNREFUSED,ex)); 32 | } 33 | else if(ex instanceof TimeoutException) { 34 | return Mono.just(new ResponseModel(ErrorEnum.BACKEND_TIMEOUT,ex)); 35 | } 36 | else if(ex instanceof SocketException) { 37 | return Mono.just(new ResponseModel(ErrorEnum.BACKEND_COMEXCEPTION,ex)); 38 | } 39 | else if(ex instanceof WebClientResponseException) { 40 | //http status code: 4xx or 5xx 41 | return Mono.just(new ResponseModel(ErrorEnum.BACKEND_4XX_5XX,ex)); 42 | } 43 | else { 44 | return Mono.just(new ResponseModel(ErrorEnum.OTHER,ex)); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/demo/http/api/gateway/access/controller/ApiProxyController.java: -------------------------------------------------------------------------------- 1 | package com.demo.http.api.gateway.access.controller; 2 | 3 | import java.time.Duration; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.core.io.buffer.DataBuffer; 9 | import org.springframework.http.HttpMethod; 10 | import org.springframework.http.server.reactive.ServerHttpRequest; 11 | import org.springframework.http.server.reactive.ServerHttpResponse; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RestController; 14 | import org.springframework.web.reactive.function.BodyInserters; 15 | import org.springframework.web.reactive.function.client.WebClient; 16 | import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec; 17 | import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec; 18 | import org.springframework.web.server.ServerWebExchange; 19 | 20 | import com.demo.http.api.gateway.constant.Constant; 21 | import com.demo.http.api.gateway.util.WebfluxForwardingUtil; 22 | 23 | import reactor.core.publisher.Mono; 24 | 25 | /** 26 | * api反向代理控制器,转发调用后端服务 27 | * 28 | * @author wang guobo 王国波 29 | */ 30 | @RestController 31 | public class ApiProxyController { 32 | WebClient webClient = WebClient.create(); 33 | 34 | //后端服务url 35 | @Value("${backend.service.url.prefix}") 36 | String backendServiceUrlPrefix; 37 | 38 | //后端服务超时 39 | @Value("${backend.service.timeout.inmillis}") 40 | long backendServiceTimeoutInMillis; 41 | 42 | /** 43 | * 44 | * @param exchange 45 | * @return 46 | */ 47 | @RequestMapping("/**") 48 | public Mono proxyRequest(ServerWebExchange exchange) 49 | { 50 | ServerHttpRequest frontEndReq = exchange.getRequest(); 51 | ServerHttpResponse frontEndResp = exchange.getResponse(); 52 | String path = frontEndReq.getPath().pathWithinApplication().value(); 53 | HttpMethod httpMethod = frontEndReq.getMethod(); 54 | RequestBodySpec reqBodySpec = webClient.method(httpMethod). 55 | uri(backendServiceUrlPrefix.concat(path)). 56 | headers(httpHeaders -> 57 | { 58 | httpHeaders.addAll(frontEndReq.getHeaders()); 59 | httpHeaders.remove("HOST"); 60 | }); 61 | 62 | RequestHeadersSpec reqHeadersSpec; 63 | if (requireHttpBody(httpMethod)) { 64 | reqHeadersSpec = reqBodySpec.body(BodyInserters.fromDataBuffers(frontEndReq.getBody())); 65 | } else { 66 | reqHeadersSpec = reqBodySpec; 67 | } 68 | 69 | //调用后端服务 70 | return reqHeadersSpec.exchange().timeout(Duration.ofMillis(backendServiceTimeoutInMillis)). 71 | onErrorResume(ex -> 72 | { 73 | //调用后端服务异常(超时、网络问题)时,转发到后端异常-后控制器 74 | //此处也可不用转发,用frontEndResp.writeWith(...)直接将异常响应写回给调用方 75 | Map forwardAttrs = new HashMap<>(); 76 | forwardAttrs.put(Constant.BACKEND_EXCEPTION_ATTR_NAME,ex); 77 | return WebfluxForwardingUtil.forward(Constant.BACKEND_EXCEPTION_PATH,exchange,forwardAttrs) 78 | .then(Mono.empty()); 79 | }).flatMap(backendResponse -> 80 | { 81 | //将后端服务的响应回写到前端resp 82 | frontEndResp.setStatusCode(backendResponse.statusCode()); 83 | frontEndResp.getHeaders().putAll(backendResponse.headers().asHttpHeaders()); 84 | return frontEndResp.writeWith(backendResponse.bodyToFlux(DataBuffer.class)); 85 | } 86 | ); 87 | } 88 | 89 | private boolean requireHttpBody(HttpMethod method) 90 | { 91 | return HttpMethod.POST == method || HttpMethod.PUT == method || 92 | HttpMethod.PATCH == method || HttpMethod.TRACE == method ; 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/demo/http/api/gateway/access/filter/RateLimitFilter.java: -------------------------------------------------------------------------------- 1 | package com.demo.http.api.gateway.access.filter; 2 | 3 | import org.springframework.core.annotation.Order; 4 | import org.springframework.stereotype.Component; 5 | import org.springframework.web.server.ServerWebExchange; 6 | import org.springframework.web.server.WebFilter; 7 | import org.springframework.web.server.WebFilterChain; 8 | import reactor.core.publisher.Mono; 9 | 10 | /** 11 | * 请求限流过滤器 12 | * 13 | * @author wang guobo 王国波 14 | */ 15 | @Order(2) 16 | @Component 17 | public class RateLimitFilter implements WebFilter { 18 | 19 | @Override 20 | public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { 21 | //doRateLimit(exchange, chain); 22 | return chain.filter(exchange); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/demo/http/api/gateway/access/filter/RequestAuthFilter.java: -------------------------------------------------------------------------------- 1 | package com.demo.http.api.gateway.access.filter; 2 | 3 | import static com.demo.http.api.gateway.constant.ErrorEnum.*; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.core.annotation.Order; 6 | import org.springframework.http.server.reactive.ServerHttpRequest; 7 | import org.springframework.stereotype.Component; 8 | import org.springframework.web.server.ServerWebExchange; 9 | import org.springframework.web.server.WebFilter; 10 | import org.springframework.web.server.WebFilterChain; 11 | import com.demo.http.api.gateway.constant.Constant; 12 | import com.demo.http.api.gateway.service.AppInfoProvider; 13 | import com.demo.http.api.gateway.util.WebfluxForwardingUtil; 14 | 15 | import reactor.core.publisher.Mono; 16 | 17 | /** 18 | * http请求认证授权过滤器 19 | * 20 | * @author wang guobo 王国波 21 | */ 22 | @Order(1) 23 | @Component 24 | public class RequestAuthFilter implements WebFilter { 25 | static final String APPKEY_HTTP_HEAD = "appKey"; 26 | 27 | @Autowired 28 | AppInfoProvider appInfoProvider; 29 | 30 | @Override 31 | public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { 32 | exchange.getAttributes().put(Constant.WEB_FILTER_ATTR_NAME,chain); 33 | if(!authRequest(exchange)) { 34 | //forward to 验证失败后controller 35 | return WebfluxForwardingUtil.forward(Constant.AUTH_FAILED_PATH, exchange,null); 36 | } 37 | else { 38 | //forward to 反向代理reverse proxy controller 39 | return chain.filter(exchange); 40 | } 41 | } 42 | 43 | /** 44 | * 请求认证、授权 45 | * @param exchange 46 | * @return 验证失败返回false,成功true 47 | */ 48 | private boolean authRequest(ServerWebExchange exchange) 49 | { 50 | ServerHttpRequest frontReq = exchange.getRequest(); 51 | 52 | String appKey = frontReq.getHeaders().getFirst(APPKEY_HTTP_HEAD); 53 | if (null == appKey) { 54 | exchange.getAttributes().put(Constant.AUTH_ERROR_ATTR_NAME,NO_APPKEY); 55 | return false; 56 | } 57 | 58 | /* 59 | AppInfo appInfo= appInfoProvider.getAppInfoByAppKey(appKey); 60 | if (null == appInfo) { 61 | serverWebExchange.getAttributes().put(Constant.VERVIFY_ERROR_ATTR_NAME,INVALID_APPKEY); 62 | return false; 63 | } 64 | */ 65 | 66 | return true; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/demo/http/api/gateway/constant/Constant.java: -------------------------------------------------------------------------------- 1 | package com.demo.http.api.gateway.constant; 2 | /** 3 | * 内部constant 4 | * 5 | * @author wang guobo 王国波 6 | */ 7 | public class Constant { 8 | //webflux controller mapping path 9 | public static final String AUTH_FAILED_PATH = "/auth/failed"; 10 | public static final String BACKEND_EXCEPTION_PATH = "/backend/exception"; 11 | 12 | public static final String WEB_FILTER_ATTR_NAME = "filterchain"; 13 | //认证失败属性名 14 | public static final String AUTH_ERROR_ATTR_NAME = "auth-error"; 15 | //后端服务异常属性名 16 | public static final String BACKEND_EXCEPTION_ATTR_NAME = "backend-exception"; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/demo/http/api/gateway/constant/ErrorEnum.java: -------------------------------------------------------------------------------- 1 | package com.demo.http.api.gateway.constant; 2 | /** 3 | * 错误码 4 | * 5 | * @author wang guobo 王国波 6 | */ 7 | public enum ErrorEnum { 8 | NO_APPKEY(350,"缺少appKey头部"),NO_SIGNATURE(351,"缺少signature"),NO_TIMESTAMP(352,"缺少timestamp"), 9 | INVALID_TIMESTAMP(353,"无效时间戳"),INVALID_SIGNATURE(354,"无效签名"), 10 | NO_AUTH(355,"鉴权失败(无权限)"),NO_SERVICE(356,"鉴权失败(无此接口)"),NOAVAILABLE_SERVICE(357,"后端无存活服务"), 11 | 12 | BACKEND_CONNREFUSED(370,"后端服务拒绝连接"),BACKEND_COMEXCEPTION(371,"网关与后端通讯异常"), 13 | BACKEND_TIMEOUT(372,"后端服务超时"), 14 | BACKEND_4XX_5XX(373,"后端http异常(4XX or 5xx status code)"),OTHER(374,"系统其他错误"); 15 | 16 | private int code; 17 | private String message; 18 | 19 | ErrorEnum(int code,String message) 20 | { 21 | this.code= code; 22 | this.message = message; 23 | } 24 | 25 | public int getCode() { 26 | return code; 27 | } 28 | 29 | public String getMessage() { 30 | return message; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/demo/http/api/gateway/dao/mapper/AppInfoMapper.java: -------------------------------------------------------------------------------- 1 | package com.demo.http.api.gateway.dao.mapper; 2 | 3 | import org.apache.ibatis.annotations.Mapper; 4 | import org.apache.ibatis.annotations.Param; 5 | import org.apache.ibatis.annotations.Select; 6 | import com.demo.http.api.gateway.model.AppInfo; 7 | /** 8 | * 接入网关的应用信息mapper 9 | * 10 | * @author wang guobo 王国波 11 | */ 12 | @Mapper 13 | public interface AppInfoMapper { 14 | @Select("SELECT app_key appKey,app_secret appSecret FROM thirdparty_app_info WHERE app_key = #{appKey}") 15 | AppInfo findByAppKey(@Param("appKey") String appKey); 16 | 17 | } -------------------------------------------------------------------------------- /src/main/java/com/demo/http/api/gateway/main/RestApiGateWayApplication.java: -------------------------------------------------------------------------------- 1 | package com.demo.http.api.gateway.main; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.context.annotation.ComponentScan; 7 | import org.springframework.web.reactive.config.EnableWebFlux; 8 | /** 9 | * http gateway main diver 10 | * 11 | * @author wang guobo 王国波 12 | */ 13 | @SpringBootApplication 14 | @EnableWebFlux 15 | @ComponentScan("com.demo.http.api.gateway") 16 | @MapperScan(value="com.demo.http.api.gateway.dao.mapper") 17 | public class RestApiGateWayApplication { 18 | 19 | public static void main(String[] args) { 20 | SpringApplication.run(RestApiGateWayApplication.class, args); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/demo/http/api/gateway/model/AppInfo.java: -------------------------------------------------------------------------------- 1 | package com.demo.http.api.gateway.model; 2 | /** 3 | * app info model 4 | * 5 | * @author wang guobo 王国波 6 | */ 7 | public class AppInfo { 8 | String appKey; 9 | //应用密钥 10 | String appSecret; 11 | 12 | public String getAppKey() { 13 | return appKey; 14 | } 15 | 16 | public void setAppKey(String appKey) { 17 | this.appKey = appKey; 18 | } 19 | 20 | public String getAppSecret() { 21 | return appSecret; 22 | } 23 | 24 | public void setAppSecret(String appSecret) { 25 | this.appSecret = appSecret; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/demo/http/api/gateway/model/ResponseModel.java: -------------------------------------------------------------------------------- 1 | package com.demo.http.api.gateway.model; 2 | 3 | import com.demo.http.api.gateway.constant.ErrorEnum; 4 | /** 5 | * response model 6 | * 7 | * @author wang guobo 王国波 8 | */ 9 | public class ResponseModel { 10 | int code; 11 | String message; 12 | boolean success = false; 13 | 14 | public ResponseModel(ErrorEnum error) 15 | { 16 | code = error.getCode(); 17 | message = error.getMessage(); 18 | } 19 | 20 | public ResponseModel(ErrorEnum error,Throwable ex) 21 | { 22 | code = error.getCode(); 23 | message = error.getMessage().concat(",").concat(ex.getMessage()); 24 | } 25 | 26 | public int getCode() { 27 | return code; 28 | } 29 | 30 | public String getMessage() { 31 | return message; 32 | } 33 | 34 | public boolean isSuccess() { 35 | return success; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/demo/http/api/gateway/service/AppInfoProvider.java: -------------------------------------------------------------------------------- 1 | package com.demo.http.api.gateway.service; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | import com.demo.http.api.gateway.dao.mapper.AppInfoMapper; 8 | import com.demo.http.api.gateway.model.AppInfo; 9 | import com.github.benmanes.caffeine.cache.Cache; 10 | import com.github.benmanes.caffeine.cache.Caffeine; 11 | /** 12 | * 应用服务 13 | * 14 | * @author wang guobo 王国波 15 | */ 16 | @Service 17 | public class AppInfoProvider { 18 | @Autowired 19 | AppInfoMapper appInfoMapper; 20 | 21 | //spring Caffeine cache 22 | Cache cache = Caffeine.newBuilder() 23 | .expireAfterWrite(10, TimeUnit.MINUTES) 24 | .maximumSize(1000) 25 | .build(); 26 | 27 | public AppInfo getAppInfoByAppKey(String appKey){ 28 | return cache.get(appKey, appInfoMapper::findByAppKey ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/demo/http/api/gateway/spring/config/DataSourceConfig.java: -------------------------------------------------------------------------------- 1 | package com.demo.http.api.gateway.spring.config; 2 | 3 | import static org.springframework.boot.jdbc.DataSourceBuilder.create; 4 | 5 | import javax.sql.DataSource; 6 | 7 | import org.mybatis.spring.SqlSessionFactoryBean; 8 | import org.springframework.beans.factory.annotation.Qualifier; 9 | import org.springframework.boot.context.properties.ConfigurationProperties; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | 13 | import com.mchange.v2.c3p0.ComboPooledDataSource; 14 | /** 15 | * spring配置类for db datasource 16 | * 17 | * @author wang guobo 王国波 18 | */ 19 | @Configuration 20 | public class DataSourceConfig { 21 | 22 | @Bean(name="dataSource") 23 | @Qualifier(value="dataSource") 24 | @ConfigurationProperties(prefix="datasource.mysql") 25 | public DataSource dataSource(){ 26 | return create().type(ComboPooledDataSource.class).build();//创建数据源 27 | } 28 | /** 29 | *返回sqlSessionFactory 30 | */ 31 | @Bean 32 | public SqlSessionFactoryBean sqlSessionFactoryBean(){ 33 | SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean(); 34 | sqlSessionFactory.setDataSource(dataSource()); 35 | return sqlSessionFactory; 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/java/com/demo/http/api/gateway/util/WebfluxForwardingUtil.java: -------------------------------------------------------------------------------- 1 | package com.demo.http.api.gateway.util; 2 | 3 | import java.util.Map; 4 | 5 | import org.springframework.http.server.reactive.ServerHttpRequest; 6 | import org.springframework.web.server.ServerWebExchange; 7 | import org.springframework.web.server.WebFilterChain; 8 | 9 | import com.demo.http.api.gateway.constant.Constant; 10 | 11 | import reactor.core.publisher.Mono; 12 | /** 13 | * webflux forward util 14 | * 15 | * @author wang guobo 王国波 16 | */ 17 | public class WebfluxForwardingUtil { 18 | /** 19 | * 20 | * @param forwardToPath: forward target path that begin with /. 21 | * @param exchange: the current source server exchange 22 | * @param forwardAttrs : the attributes that added to forward Exchange. 23 | * @return Mono to signal forwarding request completed. 24 | */ 25 | public static Mono forward(String forwardToPath,ServerWebExchange exchange,Map forwardAttrs){ 26 | WebFilterChain webFilterChain = (WebFilterChain)exchange.getAttributes().get(Constant.WEB_FILTER_ATTR_NAME); 27 | ServerHttpRequest forwardReq = exchange.getRequest().mutate().path(forwardToPath).build(); 28 | ServerWebExchange forwardExchange = exchange.mutate().request(forwardReq).build(); 29 | if(null != forwardAttrs && !forwardAttrs.isEmpty()) { 30 | forwardExchange.getAttributes().putAll(forwardAttrs); 31 | } 32 | return webFilterChain.filter(forwardExchange); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/resources/application-dev.properties: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/main/resources/application-pro.properties: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/main/resources/application-uat.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | #spring.profiles.active=dev 2 | server.port=9988 3 | 4 | backend.service.timeout.inmillis=10000 5 | backend.service.url.prefix=http://127.0.0.1:8080 6 | 7 | datasource.mysql.jdbcUrl=jdbc:mysql://127.0.0.1:3308/gateway?useUnicode=true&characterEncoding=utf-8 8 | datasource.mysql.user= 9 | datasource.mysql.password= 10 | datasource.mysql.driverClass=com.mysql.jdbc.Driver 11 | datasource.mysql.driverClass.type=com.mchange.v2.c3p0.ComboPooledDataSource 12 | datasource.mysql.testWhileIdle = true 13 | datasource.mysql.preferredTestQuery=SELECT 1 14 | datasource.mysql.testConnectionOnCheckin = true 15 | datasource.mysql.idleConnectionTestPeriod = 400 16 | datasource.mysql.maxIdleTime = 500 --------------------------------------------------------------------------------