Demo:第四章:Gateway网关
文章目录
- 前言
- 一、项目结构
- 二、步骤
- 1.pom.xml
- 2.java代码
- 总结
前言
最近搞了一套网关校验,路由分发模块,这里分享出来给大家
提示:以下是本篇文章正文内容,下面案例可供参考
一、项目结构
二、步骤
1.pom.xml
代码如下(示例):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.liaozhiwei</groupId><artifactId>gateway</artifactId><version>0.0.1-SNAPSHOT</version><name>gateway</name><description>gateway</description><properties><java.version>1.8</java.version></properties><parent><artifactId>liaozhiwei</artifactId><groupId>com.liaozhiwei</groupId><version>1.0.0</version></parent><dependencies>
<!-- 网关配置--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId><version>2.2.3.RELEASE</version></dependency>
<!-- spring cloud gateway是基于webflux的,如果非要web支持的话需要导入spring-boot-starter-webflux而不是spring-boot-start-web。--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency>
<!-- 负载均衡器--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><!-- 负载--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId></dependency><!-- nacos 依赖 开始--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><version>2.2.3.RELEASE</version></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId><version>2.2.3.RELEASE</version></dependency><!-- nacos 依赖 结束-->
<!-- 熔断--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId><version>2.0.1.RELEASE</version></dependency>
<!-- 缓存配置--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.13</version><scope>compile</scope></dependency><!--添加jwt相关的包开始--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.10.5</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.10.5</version><scope>runtime</scope></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.10.5</version><scope>runtime</scope></dependency><!--添加jwt相关的包结束-->
<!-- springboot程序的监控系统,可以实现健康检查,info信息--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.json</groupId><artifactId>json</artifactId><version>20180130</version></dependency></dependencies><build><finalName>gateway</finalName><plugins><!-- 打包生成fat jar --><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.3.4.RELEASE</version><configuration><mainClass>com.gateway.GatewayApplication</mainClass></configuration><executions><execution><phase>package</phase><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>
2.java代码
IErrorCode
package com.gateway.api;/*** 封装API的错误码*/
public interface IErrorCode {Integer getCode();String getMessage();
}
ResultCode
package com.gateway.api;/*** 枚举了一些常用API操作码*/
public enum ResultCode implements IErrorCode {SUCCESS(200, "操作成功"),FAILED(500, "操作失败"),VALIDATE_FAILED(404, "参数检验失败"),UNAUTHORIZED(401, "暂未登录或token已经过期"),AUTHORIZATION_HEADER_IS_EMPTY(600,"请求头中的token为空"),GET_TOKEN_KEY_ERROR(601,"远程获取TokenKey异常"),GEN_PUBLIC_KEY_ERROR(602,"生成公钥异常"),JWT_TOKEN_EXPIRE(603,"token校验异常"),TOMANY_REQUEST_ERROR(429,"后端服务触发流控"),BACKGROUD_DEGRADE_ERROR(604,"后端服务触发降级"),BAD_GATEWAY(502,"网关服务异常"),FORBIDDEN(403, "没有相关权限"),TOKEN_VALIDATE_FAILED(504, "token校验失败,请重新登录刷新token");private Integer code;private String message;private ResultCode(Integer code, String message) {this.code = code;this.message = message;}public Integer getCode() {return code;}public String getMessage() {return message;}
}
ResultData
package com.gateway.api;import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@Data
@NoArgsConstructor
public class ResultData<T> implements Serializable {/*** 状态码*/public boolean status = true;/*** 状态码*/private Integer code = 200;/*** 接口返回信息*/private String msg;/*** 数据对象*/private T data;/*** 初始化一个新创建的 ResultData 对象** @param status 状态码* @param msg 返回内容*/public ResultData(Boolean status, String msg) {this.status = status;this.msg = msg;}/*** 初始化一个新创建的 ResultData 对象** @param status 状态码* @param msg 返回内容* @param data 数据对象*/public ResultData(Boolean status, String msg, T data, Integer code) {this.status = status;this.msg = msg;this.data = data;this.code = code;}public ResultData(T data) {this.data = data;}/*** 返回成功消息** @param msg 返回内容* @param data 数据对象* @return 成功消息*/public static <T> ResultData<T> success(String msg, T data) {return new ResultData<T>(true, msg, data, 200);}/*** 返回成功消息** @param msg 返回内容* @return 成功消息*/public static <T> ResultData<T> success(String msg) {return ResultData.success(msg, null);}/*** 返回成功消息** @return 成功消息*/public static <T> ResultData<T> success() {return ResultData.success(null);}/*** 返回成功数据** @return 成功消息*/public static <T> ResultData<T> success(T data) {return ResultData.success(null, data);}/*** 返回错误消息** @return*/public static <T> ResultData<T> error() {return ResultData.error(null);}/*** 返回错误消息** @param msg 返回内容* @return 警告消息*/public static <T> ResultData<T> error(String msg) {return ResultData.error(msg, null);}/*** 返回错误消息** @param code 状态码* @param msg 返回内容* @return 警告消息*/public static <T> ResultData<T> error(Integer code, String msg) {return new ResultData<T>(false, msg, null, code);}/*** 返回错误消息** @param msg 返回内容* @param data 数据对象* @return 警告消息*/public static <T> ResultData<T> error(String msg, T data) {return new ResultData<T>(false, msg, data, 500);}
}
RedisConfig
/*** Copyright (c) 2016-2019 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package com.gateway.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** Redis配置** @author zhiwei liao*/
@Configuration
public class RedisConfig {@Autowiredprivate RedisConnectionFactory factory;@Beanpublic RedisTemplate<String, Object> redisTemplate() {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(new StringRedisSerializer());redisTemplate.setConnectionFactory(factory);return redisTemplate;}@Beanpublic HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForHash();}@Beanpublic ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {return redisTemplate.opsForValue();}@Beanpublic ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForList();}@Beanpublic SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForSet();}@Beanpublic ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForZSet();}
}
RibbonConfig
package com.gateway.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;import java.util.Collections;@Configuration
public class RibbonConfig {@Autowiredprivate LoadBalancerClient loadBalancer;@Bean//@LoadBalanced SmartInitializingSingleton InitializingBean (构建bean的init方法)// 顺序的问题 SmartInitializingSingleton是在所有的非懒加载单例bean构建完成之后调用的public RestTemplate restTemplate(){RestTemplate restTemplate = new RestTemplate();restTemplate.setInterceptors(Collections.singletonList(new LoadBalancerInterceptor(loadBalancer)));return restTemplate;}}
ApplicationConstant
package com.gateway.constant;/*** @Description 应用常量* @Author zhiwei Liao* @Date 2021/6/15 11:06**/
public class ApplicationConstant {//本地环境public static final String LOCAL_ENVIRONMENT = "local";//开发环境public static final String DEV_ENVIRONMENT = "dev";//测试环境public static final String UAT_ENVIRONMENT = "uat";//正式环境public static final String PRO_ENVIRONMENT = "pro";//public static final String UAT2_ENVIRONMENT = "uat2";
}
GateWayConstant
package com.gateway.constant;/*** @author zhiwei Liao* @version 1.0* @Description* @Date 2021/8/2 15:26*/public class GateWayConstant {public static final String KEY = "iA0`bN0&lKJ3{vH0(";public static final String TOKEN = "token:";public static final long TOKEN_EXPIRE_TIME = 86400000;public static final String REQUEST_TIME_BEGIN = "======请求开始时间:\n {}";public static final String REQUEST_TIME_END = "======请求开始时间:\n {}";public static final String REQUEST_GET = "=======GET请求:\n {}";public static final String REQUEST_POST = "======POST请求:\n {}";public static final String REQUEST_POST_TIME = "======POST请求:\n {}";public static final String URL_REQUIRING_AUTHENTICATION = "======需要认证的URL:{}:\n ";public static final String SKIP_CERTIFIED_URL = "======跳过认证的URL:{}:\n ";
}
GateWayException
package com.gateway.exception;import com.gateway.api.IErrorCode;
import lombok.Data;@Data
public class GateWayException extends RuntimeException{private long code;private String message;public GateWayException(IErrorCode iErrorCode) {this.code = iErrorCode.getCode();this.message = iErrorCode.getMessage();}
}
HttpResponseFilter
package com.b8.gateway.filter;import org.json.JSONTokener;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.utils.StringUtils;
import com.gateway.api.ResultCode;
import com.gateway.api.ResultData;
import com.gateway.constant.ApplicationConstant;
import com.gateway.constant.GateWayConstant;
import com.gateway.properties.NotAuthUrlProperties;
import com.gateway.util.JsonUtils;
import com.gateway.util.JwtUtils;
import com.gateway.util.RedisUtil;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.DigestUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.PathMatcher;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import java.security.PublicKey;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @author zhiwei liao* @version 1.0* @Description* @Date 2021/5/21 18:30*/
@Component
@Order(0)
@EnableConfigurationProperties(value = NotAuthUrlProperties.class)
public class HttpResponseFilter implements GlobalFilter, InitializingBean {protected final static String parameterReg = "-{28}([0-9]{24})\r\n.+name=\"(\\S*)\"\r\n\r\n(\\S*)";protected final static String fileParameterReg = "-{28}([0-9]{24})\r\n.+name=\"(\\S*)\"; filename=\"(\\S*)\"\r\n.*\r\n\r\n";private Logger log = LoggerFactory.getLogger(HttpResponseFilter.class);/*** jwt的公钥,需要网关启动,远程调用认证中心去获取公钥*/private PublicKey publicKey;@Autowiredprivate RestTemplate restTemplate;/*** 请求各个微服务 不需要用户认证的URL*/@Autowiredprivate NotAuthUrlProperties notAuthUrlProperties;//开发环境:dev开发,uat测试@Value("${environment}")private String environment;@Value("${dev_environment}")private String devEnvironment;@Value("${uat_environment}")private String uatEnvironment;@Value("${uat2_environment}")private String uat2Environment;@Value("${local_environment}")private String localEnvironment;@Overridepublic void afterPropertiesSet() throws Exception {log.info("===========环境类型:" + environment);String authTokenKeyIp = null;if(environment.equals(ApplicationConstant.LOCAL_ENVIRONMENT)){authTokenKeyIp = localEnvironment;}else if(environment.equals(ApplicationConstant.DEV_ENVIRONMENT)){authTokenKeyIp = devEnvironment;}else if(environment.equals(ApplicationConstant.UAT_ENVIRONMENT)){authTokenKeyIp = uatEnvironment;}else if(environment.equals(ApplicationConstant.UAT2_ENVIRONMENT)){authTokenKeyIp = uat2Environment;}else{authTokenKeyIp = devEnvironment;}//获取公钥 http://127.0.0.1:9013/oauth/token_keythis.publicKey = JwtUtils.genPulicKey(restTemplate,authTokenKeyIp);}private boolean shouldSkip(String currentUrl) {//路径匹配器(简介SpringMvc拦截器的匹配器)//比如/oauth/** 可以匹配/oauth/token /oauth/check_token等PathMatcher pathMatcher = new AntPathMatcher();for(String skipPath:notAuthUrlProperties.getShouldSkipUrls()) {if(pathMatcher.match(skipPath,currentUrl)) {return true;}}return false;}private ServerHttpRequest wrapHeader(ServerWebExchange serverWebExchange,Claims claims) {String loginUserInfo = JSON.toJSONString(claims);log.info("jwt的用户信息:{}",loginUserInfo);
// String userId = claims.get("additionalInfo", Map.class).get("userId").toString();String userName = claims.get("additionalInfo",Map.class).get("userName").toString();String nickName = claims.get("additionalInfo",Map.class).get("nickName").toString();
// String loginType = claims.get("additionalInfo",Map.class).get("loginType").toString();//向headers中放文件,记得buildServerHttpRequest request = serverWebExchange.getRequest().mutate()
// .header("userId",userId).header("userName",userName).header("nickName",nickName)
// .header("loginType",loginType).build();return request;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {log.info(GateWayConstant.REQUEST_TIME_BEGIN, new Date());ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());//获取参数类型String contentType = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);log.info("======content type:{}", contentType);// 解析参数OAuthRequestFactory requestFactory = new WebFluxOAuthRequestFactory();OAuthRequest authRequest = requestFactory.createRequest(exchange.getRequest());Map<String, String> requestParamsMap = new HashMap<>();exchange.getAttributes().put(GateWayConstant.REQUEST_TIME_BEGIN, System.currentTimeMillis());HttpHeaders headers = new HttpHeaders();headers.putAll(exchange.getRequest().getHeaders());headers.remove(HttpHeaders.CONTENT_LENGTH);ServerHttpRequest serverHttpRequest = exchange.getRequest();//校验请求Mono<Void> check = check(headers, exchange, serverHttpRequest);if (check != null) {log.warn("======check未通过: {}", check);return check;}//1.过滤不需要认证的url,比如/oauth/**String currentUrl = exchange.getRequest().getURI().getPath();//过滤不需要认证的urlif(shouldSkip(currentUrl)) {log.info(GateWayConstant.SKIP_CERTIFIED_URL,currentUrl);}else {log.info(GateWayConstant.URL_REQUIRING_AUTHENTICATION,currentUrl);//2. 获取token,从请求头中解析 Authorization value: bearer xxxxxxx或者从请求参数中解析 access_token//第一步:解析出我们Authorization的请求头 value为: “bearer XXXXXXXXXXXXXX”String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");String acceptLanguage = exchange.getRequest().getHeaders().getFirst("accept-language");//第二步:判断Authorization的请求头是否为空if(StringUtils.isEmpty(authHeader)) {log.warn("======需要认证的url,请求头为空");ResultData resultData = new ResultData();resultData.setStatus(false);resultData.setCode(HttpStatus.UNAUTHORIZED.value());String msg;if("en_us".equals(acceptLanguage)){msg = "Unauthorized";}else if("pl_pl".equals(acceptLanguage)){msg = "nieupowa?nione";}else if("zh_cn".equals(acceptLanguage)){msg = "未授权";}else {msg = "Unauthorized";}resultData.setMsg(msg);return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(Objects.requireNonNull(JsonUtils.toJson(resultData)).getBytes())));}//3. 校验token,拿到token后,通过公钥(需要从授权服务获取公钥)校验,校验失败或超时抛出异常//第三步 校验我们的jwt 若jwt不对或者超时都会抛出异常Claims claims = JwtUtils.validateJwtToken(authHeader,publicKey);if(claims == null){log.warn("======校验jwt,jwt不对");ResultData resultData = new ResultData();resultData.setStatus(false);resultData.setCode(ResultCode.TOKEN_VALIDATE_FAILED.getCode());String msg;if("en_us".equals(acceptLanguage)){msg = "token validate failed";}else if("pl_pl".equals(acceptLanguage)){msg = "token validate nie powiod?o si?";}else if("zh_cn".equals(acceptLanguage)){msg = "token校验失败";}else {msg = "token validate failed";}resultData.setMsg(msg);return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(Objects.requireNonNull(JsonUtils.toJson(resultData)).getBytes())));}//4. 校验通过后,从token中获取的用户登录信息存储到请求头中//第四步 把从jwt中解析出来的 用户登陆信息存储到请求头中ServerHttpRequest httpRequest = wrapHeader(exchange, claims);headers.putAll(httpRequest.getHeaders());}Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).publishOn(Schedulers.immediate()).flatMap(originalBody -> {// 根据请求头,用不同的方式解析Bodyif (StringUtils.isNotEmpty(contentType)) {if (contentType.startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) {this.parseRequestBody(requestParamsMap, originalBody);} else if (contentType.startsWith(MediaType.APPLICATION_JSON_VALUE)) {this.parseRequestJson(requestParamsMap, originalBody);} else if (contentType.startsWith(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {this.parseRequestQuery(requestParamsMap, originalBody);}}// 加载QueryParameterthis.parseRequestQuery(requestParamsMap, exchange.getRequest().getQueryParams());log.info("所有参数:{}", JSON.toJSONString(requestParamsMap));// 把信息放置到线程容器内authRequest.setParameters(requestParamsMap);OAuthRequestContainer.set(authRequest);return Mono.just(originalBody);});log.info("所有参数:{}", JSON.toJSONString(requestParamsMap));// 把修改过的参数、消费过的参数,重新封装发布BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);Mono<Void> result = bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {ServerHttpRequest decorator = decorate(exchange, headers, outputMessage);return chain.filter(exchange.mutate().request(decorator).build());})).onErrorResume((Function<Throwable, Mono<Void>>)throwable -> release(exchange, outputMessage, throwable));log.info(GateWayConstant.REQUEST_TIME_END, new Date());return result;}/*** 校验参数** @param headers* @return*/private Mono<Void> check(HttpHeaders headers, ServerWebExchange exchange, ServerHttpRequest serverHttpRequest) {String timestamp = headers.getFirst("timestamp");if (StringUtils.isEmpty(timestamp)) {log.info("========timestamp为空");return resultExchange(exchange);} else {log.info("=========timestamp:" + timestamp);}String acceptLanguage = headers.getFirst("accept-language");if (StringUtils.isEmpty(acceptLanguage)) {log.info("========acceptLanguage为空");return resultExchange(exchange);} else {log.info("=========acceptLanguage:" + acceptLanguage);}String vcode = headers.getFirst("vcode");if (StringUtils.isEmpty(vcode)) {log.info("========vcode为空");return resultExchange(exchange);} else {log.info("=========vcode:" + vcode);log.info("=========key:" + GateWayConstant.KEY);String keyMd5 = GateWayConstant.KEY + timestamp;String generatorVcode = DigestUtils.md5DigestAsHex(keyMd5.getBytes());log.info("=========generatorVcode:" + generatorVcode);if (!vcode.equals(generatorVcode)) {log.info("===========vcode校验不对");return resultExchange(exchange);}}//校验是否重复提交String commitRedisKey = GateWayConstant.TOKEN + vcode + serverHttpRequest.getURI().getRawPath();//加锁boolean success = RedisUtil.getLock(commitRedisKey, commitRedisKey, 1);if (!success) {log.info("=========请求太快了!请稍后再试!");return resultExchange(exchange);} else {//释放锁RedisUtil.releaseLock(commitRedisKey, commitRedisKey);}return null;}/*** @param exchange* @return Mono<Void>* @Description 定义拦截返回状态码* @Author zhiwei Liao* @Date 2021/5/21/14:56*/private Mono<Void> resultExchange(ServerWebExchange exchange) {//定义拦截返回状态码ResultData resultData = new ResultData();resultData.setStatus(false);resultData.setCode(HttpStatus.NOT_ACCEPTABLE.value());resultData.setMsg(HttpStatus.NOT_ACCEPTABLE.getReasonPhrase());return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(Objects.requireNonNull(JsonUtils.toJson(resultData)).getBytes())));}protected void parseRequestBody(Map<String, String> parameterMap, String parameterString) {this.regexParseBodyString(parameterReg, parameterMap, parameterString);this.regexParseBodyString(fileParameterReg, parameterMap, parameterString);}protected void parseRequestJson(Map<String, String> parameterMap, String parameterString) {Object json = new JSONTokener(parameterString).nextValue();if(json instanceof JSONObject){JSONObject object = (JSONObject)json;for (String key : object.keySet()) {parameterMap.put(key, object.getString(key));}}else if (json instanceof JSONArray){JSONArray jsonArray = (JSONArray)json;for (Object value : jsonArray) {parameterMap.put(null,(String)value);}}}protected void parseRequestQuery(Map<String, String> parameterMap, MultiValueMap<String, String> queryParamMap) {if (queryParamMap != null && !queryParamMap.isEmpty()) {for (String key : queryParamMap.keySet()) {final List<String> stringList = queryParamMap.get(key);parameterMap.put(key, stringList != null && !stringList.isEmpty() ? StringUtils.join(Arrays.asList(stringList.toArray()), ",") : null);}}}protected void parseRequestQuery(Map<String, String> parameterMap, String parameterString) {final String[] paramsStr = parameterString.split("&");for (String s : paramsStr) {log.info("请求名:" + s.split("=")[0]);log.info("请求值:" + s.split("=")[1]);parameterMap.put(s.split("=")[0], s.split("=")[1]);}}protected void regexParseBodyString(String reg, Map<String, String> parameterMap, String bodyStr) {Matcher matcher = Pattern.compile(reg).matcher(bodyStr);while (matcher.find()) {parameterMap.put(matcher.group(2), matcher.group(3));log.info("请求参数编号:" + matcher.group(1));log.info("请求名:" + matcher.group(2));log.info("请求值:" + matcher.group(3));}}protected ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers,CachedBodyOutputMessage outputMessage) {return new ServerHttpRequestDecorator(exchange.getRequest()) {@Overridepublic HttpHeaders getHeaders() {long contentLength = headers.getContentLength();HttpHeaders httpHeaders = new HttpHeaders();httpHeaders.putAll(super.getHeaders());if (contentLength > 0) {httpHeaders.setContentLength(contentLength);} else {httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");}return httpHeaders;}@Overridepublic Flux<DataBuffer> getBody() {return outputMessage.getBody();}};}protected Mono<Void> release(ServerWebExchange exchange,CachedBodyOutputMessage outputMessage, Throwable throwable) {// if (outputMessage.isCached()) {// return outputMessage.getBody().map(DataBufferUtils::release)
// .then(Mono.error(throwable));
// }return Mono.error(throwable);}
}
OAuthRequest
package com.gateway.filter;import java.util.Map;
import java.util.Set;public class OAuthRequest {/*** 请求参数*/private Map<String, String> parameters;/*** 请求头*/private Map<String, String> headers;/*** 请求方式:POST、GET、PUT、DELETE*/private String method;/*** 请求全路径*/private String requestURL;/*** 请求路径*/private String requestURI;/*** 请求地址参数*/private String queryString;/*** 请求来源地址*/private String remoteHost;public OAuthRequest() {}public OAuthRequest(Map<String, String> parameters, Map<String, String> headers, String method, String requestURL, String requestURI, String queryString, String remoteHost) {this.parameters = parameters;this.headers = headers;this.method = method;this.requestURL = requestURL;this.requestURI = requestURI;this.queryString = queryString;this.remoteHost = remoteHost;}/*** 获取请求参数** @param name 参数名* @return 请求参数*/public String getParameter(String name) {return parameters.get(name);}public Map<String, String> getParameters() {return parameters;}public OAuthRequest setParameters(Map<String, String> parameters) {this.parameters = parameters;return this;}/*** 获取请求头** @param name 参数名* @return 请求头信息*/public String getHeader(String name) {return headers.get(name);}public Map<String, String> getHeaders() {return headers;}public OAuthRequest setHeaders(Map<String, String> headers) {this.headers = headers;return this;}public String getMethod() {return method;}public OAuthRequest setMethod(String method) {this.method = method;return this;}public String getRequestURL() {return requestURL;}public OAuthRequest setRequestURL(String requestURL) {this.requestURL = requestURL;return this;}public String getRequestURI() {return requestURI;}public OAuthRequest setRequestURI(String requestURI) {this.requestURI = requestURI;return this;}public String getQueryString() {return queryString;}public OAuthRequest setQueryString(String queryString) {this.queryString = queryString;return this;}public String getRemoteHost() {return remoteHost;}public OAuthRequest setRemoteHost(String remoteHost) {this.remoteHost = remoteHost;return this;}public OAuthRequest narrowScope(Set<String> scope) {this.parameters.put("scope", String.join(",", scope.toArray(new String[]{})));return this;}
}
OAuthRequestContainer
package com.gateway.filter;public class OAuthRequestContainer {private static ThreadLocal<OAuthRequest> local = new InheritableThreadLocal<>();private OAuthRequestContainer() {}public static void set(OAuthRequest request) {local.set(request);}public static OAuthRequest get() {return local.get();}public static void remove() {local.remove();}public static void rewriteOAuthRequestContainer(ThreadLocal<OAuthRequest> request) {local = request;}
}
OAuthRequestFactory
package com.gateway.filter;import com.alibaba.nacos.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;/*** @desc: 请求解析工厂类*/
public abstract class OAuthRequestFactory {private static final Logger logger = LoggerFactory.getLogger(OAuthRequestFactory.class);/*** 构造请求实体** @param httpRequest SpringMvc下传入HttpServletRequest* @return {@link OAuthRequest} 请求实体*/public abstract OAuthRequest createRequest(Object httpRequest);/*** 构造封装请求实体** @param headers 请求头信息* @param parameters 请求参数* @param remoteHost 请求来源IP* @param method 请求方式:POST、GET...* @param requestURL 请求全路径* @param requestURI 请求路径* @param queryString 请求路径参数*/protected OAuthRequest buildRequest(Map<String, String> parameters, Map<String, String> headers, String method, String requestURL, String requestURI, String queryString, String remoteHost) {final String token = headers.get("HEADER_TOKEN.toLowerCase()");final String clientToken = headers.get("HEADER_TOKEN.toLowerCase()");// 判断是否包含认证OAuthAuthentication字段if (StringUtils.isNotEmpty(token)) {// TODO 解析令牌//final OAuthAuthentication authentication = resourceServerTokenServices.loadAuthentication(token);if (StringUtils.isNotEmpty(clientToken)) {// TODO 解析请求Client令牌}return new OAuthRequest(parameters, headers, method, requestURL, requestURI, queryString, remoteHost);}return new OAuthRequest(parameters, headers, method, requestURL, requestURI, queryString, remoteHost);}
}
WebFluxOAuthRequestFactory
package com.gateway.filter;import com.alibaba.nacos.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import java.net.URI;
import java.util.*;public class WebFluxOAuthRequestFactory extends OAuthRequestFactory {private static final Logger logger = LoggerFactory.getLogger(WebFluxOAuthRequestFactory.class);/*** 构造请求实体** @param httpRequest SpringMvc下传入HttpServletRequest* @return {@link OAuthRequest} 请求实体*/@Overridepublic OAuthRequest createRequest(Object httpRequest) {ServerHttpRequest request = (ServerHttpRequest) httpRequest;final String sourceIp = analysisSourceIp(request);final URI uri = request.getURI();final String url = uri.getHost() + ":" + uri.getPort() + uri.getPath() + "?" + uri.getQuery();final Map<String, String> headersMap = getHeadersMap(request);return this.buildRequest(null, headersMap, request.getMethodValue().toUpperCase(), url, uri.getPath(), uri.getQuery(), sourceIp);}/*** 获取客户端真实IP*/protected String analysisSourceIp(ServerHttpRequest request) {String ip = null;//X-Forwarded-For:Squid 服务代理String ipAddresses = request.getHeaders().getFirst("X-Forwarded-For");if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { //Proxy-Client-IP:apache 服务代理ipAddresses = request.getHeaders().getFirst("Proxy-Client-IP");}if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { //WL-Proxy-Client-IP:weblogic 服务代理ipAddresses = request.getHeaders().getFirst("WL-Proxy-Client-IP");}if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { //HTTP_CLIENT_IP:有些代理服务器ipAddresses = request.getHeaders().getFirst("HTTP_CLIENT_IP");}if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) { //X-Real-IP:nginx服务代理ipAddresses = request.getHeaders().getFirst("X-Real-IP");} //有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IPif (ipAddresses != null && ipAddresses.length() != 0) {ip = ipAddresses.split(",")[0];} //还是不能获取到,最后再通过request.getRemoteAddr();获取if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {ip = Objects.requireNonNull(request.getRemoteAddress()).getHostString();}return ip;}/*** 获取所有Header信息*/private Map<String, String> getHeadersMap(ServerHttpRequest request) {final HashMap<String, String> headerMap = new HashMap<>();for (String key : request.getHeaders().keySet()) {final List<String> stringList = request.getHeaders().get(key);headerMap.put(key, stringList != null && !stringList.isEmpty() ? StringUtils.join(Arrays.asList(stringList.toArray()), ",") : null);}return headerMap;}
}
NotAuthUrlProperties
package com.gateway.properties;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;import java.util.LinkedHashSet;@Data
@ConfigurationProperties("auth.gateway")
public class NotAuthUrlProperties {private LinkedHashSet<String> shouldSkipUrls;
}
JsonUtils
/** Licensed to the Apache Software Foundation (ASF) under one or more* contributor license agreements. See the NOTICE file distributed with* this work for additional information regarding copyright ownership.* The ASF licenses this file to You under the Apache License, Version 2.0* (the "License"); you may not use this file except in compliance with* the License. You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.gateway.util;import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;/*** JsonUtils.*/
@Slf4j
public final class JsonUtils {private static final ObjectMapper MAPPER = new ObjectMapper();static {JavaTimeModule javaTimeModule = new JavaTimeModule();javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).disable(SerializationFeature.FAIL_ON_EMPTY_BEANS).configure(JsonParser.Feature.ALLOW_COMMENTS, true).configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true).configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true).configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true).setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).registerModule(javaTimeModule).addMixIn(Map.class, IgnoreType.class);}/*** To json string.** @param object the object* @return the string*/public static String toJson(final Object object) {try {return MAPPER.writeValueAsString(object);} catch (IOException e) {log.warn("write to json string error: " + object, e);return "{}";}}/*** Remove class object.** @param object the object* @return the object*/public static Object removeClass(final Object object) {if (object instanceof Map) {Map<?, ?> map = (Map<?, ?>) object;Object result = map.get("result");if (result instanceof Map) {Map<?, ?> resultMap = (Map<?, ?>) result;resultMap.remove("class");}map.remove("class");}return object;}@JsonIgnoreProperties("class")@interface IgnoreType {}
}
JwtUtils
package com.gateway.util;import com.gateway.api.ResultCode;
import com.gateway.exception.GateWayException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.springframework.http.*;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;@Slf4j
public class JwtUtils {/*** 认证服务器许可我们的网关的clientId(需要在oauth_client_details表中配置)*/private static final String CLIENT_ID = "b8-gateway";/*** 认证服务器许可我们的网关的client_secret(需要在oauth_client_details表中配置)*/private static final String CLIENT_SECRET = "a4d4aa1";/*** 认证服务器暴露的获取token_key的地址*/private static final String AUTH_TOKEN_KEY_URL = ":9006/oauth/token_key";/*** 请求头中的 token的开始*/private static final String AUTH_HEADER = "bearer ";public static void main(String[] args) {//密码加密方式BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();String b0h2a2 = bCryptPasswordEncoder.encode("604428249078181888");System.out.println(b0h2a2);}/*** 方法实现说明: 通过远程调用获取认证服务器颁发jwt的解析的key* @author:smlz* @param restTemplate 远程调用的操作类* @return: tokenKey 解析jwt的tokenKey* @exception:* @date:2020/1/22 11:31*/private static String getTokenKeyByRemoteCall(RestTemplate restTemplate,String ip) throws Exception {//第一步:封装请求头HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);headers.setBasicAuth(CLIENT_ID,CLIENT_SECRET);HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(null, headers);//第二步:远程调用获取token_keytry {ResponseEntity<Map> response = restTemplate.exchange(ip + AUTH_TOKEN_KEY_URL, HttpMethod.GET, entity, Map.class);String tokenKey = response.getBody().get("value").toString();log.info("去认证服务器获取Token_Key:{}",tokenKey);return tokenKey;}catch (Exception e) {log.error("远程调用认证服务器获取Token_Key失败:{}",e.getMessage());throw new Exception(ResultCode.GET_TOKEN_KEY_ERROR.getMessage());}}/*** 方法实现说明:生成公钥* @author:smlz* @param restTemplate:远程调用操作类* @return: PublicKey 公钥对象* @exception:* @date:2020/1/22 11:52*/public static PublicKey genPulicKey(RestTemplate restTemplate,String ip) throws Exception {String tokenKey = getTokenKeyByRemoteCall(restTemplate,ip);try{//把获取的公钥开头和结尾替换掉String dealTokenKey =tokenKey.replaceAll("\\-*BEGIN PUBLIC KEY\\-*", "").replaceAll("\\-*END PUBLIC KEY\\-*", "").trim();java.security.Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(dealTokenKey));KeyFactory keyFactory = KeyFactory.getInstance("RSA");PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);log.info("生成公钥:{}",publicKey);return publicKey;}catch (Exception e) {log.info("生成公钥异常:{}",e.getMessage());throw new Exception(ResultCode.GEN_PUBLIC_KEY_ERROR.getMessage());}}/*** @Description 校验token* @MethodParameterTypes [java.lang.String, java.security.PublicKey]* @MethodParameters [authHeader, publicKey]* @MethodReturnType io.jsonwebtoken.Claims* @Author zhiwei Liao* @Date 2021/8/23 11:40**/public static Claims validateJwtToken(String authHeader,PublicKey publicKey) throws GateWayException {String token = null ;try{token = StringUtils.substringAfter(authHeader, AUTH_HEADER);Jwt<JwsHeader, Claims> parseClaimsJwt = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);Claims claims = parseClaimsJwt.getBody();log.info("claims:{}",claims);return claims;}catch(Exception e){log.error("校验token异常:{},异常信息:{}",token,e.getMessage());return null;}}
}
RedisUtil
package com.gateway.util;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Collections;/*** @author zhiwei Liao* @version 1.0* @Description* @Date 2021/8/2 16:51*/@Component
public class RedisUtil {@Autowiredprivate RedisTemplate redisTemplate;public static RedisTemplate redis;private static final String GET_LOCK_SCRIPT = "if redis.call('setNx',KEYS[1],ARGV[1]) then if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end end";private static final String RELEASE_LOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";@PostConstructpublic void getRedisTemplate(){redis = this.redisTemplate;}/*** 加锁* @param lockKey* @param value* @param expireTime 默认是秒* @return*/public static boolean getLock(String lockKey, String value, int expireTime){boolean ret = false;try{DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(GET_LOCK_SCRIPT, Long.class);Object result = RedisUtil.redis.execute(redisScript,new StringRedisSerializer(),new StringRedisSerializer(), Collections.singletonList(lockKey),value,String.valueOf(expireTime));ret = "1".equals(result.toString()) ;return ret;}catch(Exception e){e.printStackTrace();}finally {RedisConnectionUtils.unbindConnection(RedisUtil.redis.getConnectionFactory());}return ret;}/*** 释放锁* @param lockKey* @param value* @return*/public static boolean releaseLock(String lockKey, String value) {boolean ret = false;try{DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(RELEASE_LOCK_SCRIPT, Long.class);Object result = RedisUtil.redis.execute(redisScript, new StringRedisSerializer(), new StringRedisSerializer(),Collections.singletonList(lockKey), value);ret = "1".equals(result.toString()) ;}catch(Exception e){e.printStackTrace();}finally {RedisConnectionUtils.unbindConnection(RedisUtil.redis.getConnectionFactory());}return ret;}
}
B8GatewayApplication
package com.gateway;import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;@EnableDiscoveryClient
@SpringCloudApplication
public class B8GatewayApplication {public static void main(String[] args) {SpringApplication.run(B8GatewayApplication.class, args);System.out.println("=======网关服务启动成功================");}@Bean@LoadBalancedRestTemplate restTemplate() {return new RestTemplate();}
}
bootstrap.yml
server:port: 10002
spring:mvc:async:request-timeout: 20000static-path-pattern: /**application:# 应用名称name: gatewaymain:allow-bean-definition-overriding: trueapplication_key: iA0`bN0&lK0_H0(dev_environment: http://x.xx.xx.xxx
uat_environment: http://x.xx.xx.xxx
uat2_environment: http://x.xx.xx.xxx
local_environment: http://x.xx.xx.xxxb8auth:gateway:shouldSkipUrls:- /user/xx- /xx/xx
bootstrap-uat2.yml
# Spring
spring:cloud:nacos:discovery:# 服务注册地址server-addr: ${NACOSHOST:http://xx.xx.xx.xxx}:${NACOSPORT:8034}config:# 配置中心地址server-addr: ${spring.cloud.nacos.discovery.server-addr}# 配置文件格式file-extension: yml# 共享配置shared-configs:- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}gateway:discovery:locator:enabled: falselowerCaseServiceId: trueroutes:- id: useruri: lb://userpredicates:- Path=/user/**filters:- StripPrefix=1- id: contenturi: lb://xxpredicates:- Path=/xx/**filters:- StripPrefix=1- id: contentPushuri: lb://xxpredicates:- Path=/xx/**filters:- StripPrefix=1redis:database: 0host: x.xx.xx.xxxport: 8901# host: xx.xx.xx.xx# port: 38764password: bU0@rR0\!dE7:*oFdfafsddfslettuce:pool:min-idle: 8max-idle: 500max-active: 2000max-wait: 10000timeout: 5000
logging:level:root: infocom.gateway: debugenvironment: uat2
application_key: i`bNdfasfasK0_lJ3{vH0(
总结
以上就是今天要讲的内容,本文仅仅简单介绍了gateway网关的实现,目前当前功能可以拿去直接上线使用,企业级,已实现路由转发,参数校验,配置中心。可以配合授权模块使用:https://liaozhiwei.blog.csdn.net/article/details/120291130 当然里面有些写死的东西,需要大家改成自个的。
Demo:第四章:Gateway网关相关推荐
- 【.NET Core项目实战-统一认证平台】第四章 网关篇-数据库存储配置(2)
[.NET Core项目实战-统一认证平台]第四章 网关篇-数据库存储配置(2) 原文:[.NET Core项目实战-统一认证平台]第四章 网关篇-数据库存储配置(2) [.NET Core项目实战- ...
- SpringCloud Greenwich(四)注册中心之eureka、Zuul和 gateway网关配置
本项目是搭建基于eureka注册中心的springcloud,使用zuul网关和gateway网关 一.框架搭建 (1)项目结构 eureka-server eureka注册中心 micro-ser ...
- 《深入理解 Spring Cloud 与微服务构建》第十一章 服务网关
<深入理解 Spring Cloud 与微服务构建>第十一章 服务网关 文章目录 <深入理解 Spring Cloud 与微服务构建>第十一章 服务网关 一.服务网关简介 二. ...
- SpringCloud系列教程(五)之SpringCloud Gateway 网关聚合开发文档 swagger knife4j 和登录权限统一验证【Hoxton版】
阅读提醒: 本文面向的是有一定springboot基础者 本次教程使用的Spring Cloud Hoxton RELEASE版本 由于knife4j比swagger更加友好,所以本文集成knife4 ...
- 鸟哥的Linux私房菜(服务器)- 第四章、连上 Internet
第四章.连上 Internet 最近更新日期:2011/07/20 终于要来到修改 Linux 网络参数的章节了!在第二章的网络基础中, 我们知道主机要连上 Internet 需要一些正确的网络参数设 ...
- 计算机网络总结:第四章 网络层
第四章 网络层 4.1 概述 4.1.1 转发和路由选择 转发(forwarding):当一个分组到达路由器的一条输入链路时,路由器必须将该分组移动到适当的输出链路 路由选择(routing):当分组 ...
- spring cloud gateway 网关_微服务网关Spring Cloud Gateway全搞定
一.微服务网关Spring Cloud Gateway 1.1 导引 文中内容包含:微服务网关限流10万QPS.跨域.过滤器.令牌桶算法. 在构建微服务系统中,必不可少的技术就是网关了,从早期的Zuu ...
- 第十一、十二、十三、十四章 网络配置管理、归档和远程复制同步文件、软件包管理、创建访问linux文件系统
第十一章 网络配置管理 网络地址获取方式: 1)DHCP自动获取 2)手动配置 1.网卡配置文件: /etc/sysconfig/network-scripts/ [root@server0 Desk ...
- 第四章web服务器之httpd
文章目录 第四章 web服务器 1.1 www简介 1.1.1 网址及HTTP简介 1.1.2 HTTP协议请求的工作流程 1.2 www服务器的类型 1.2.1 仅提供用户浏览的单向静态网页 1.2 ...
- 计算机网络复习题第四章(标有课本知识点)
选择题 1.如果子网掩码是 255.255.255.224,那么主机 218.22.50.40 所在的驻留子网是( C). A.218.22.50.0 B.218.22.50.224 C.218.22 ...
最新文章
- Swing编程基础 之四
- 研发团队资源成本优化实践
- 数据的结构和运算(求和,最大和最小)
- 减少C++编译时间的方法
- 小程序开发(1)-之目录结构和文件说明
- 如何在回调中访问正确的“ this”?
- POJ 3046 Ant Counting ( 多重集组合数 经典DP )
- Get Hardware ID
- cocos2dx 手游luac 解密 文件的key
- 企业业务流程管理软件功能简介
- mentohust mac安装
- Python int()使用小结
- WPF Ribbon UI插件式开发框架
- 睿智的目标检测32——TF2搭建YoloV4目标检测平台(tensorflow2)
- 怎么将图片压缩到50k以内?教你一招将图片缩小的方法
- 决策树系列之一决策树的入门教程
- 用户行为分析zhi应用分析模型
- ROG 幻16无线网卡驱动无法更新
- 标签类目 知识图谱_短视频标签体系
- Python爬取网站数据并存入数据库(简单易懂)
热门文章
- 常考的java数据库笔试题
- FNT不能计算通用数据集(看看fwt···)
- 中望3D 2021 “坐标标注”
- 最新版 银图/网银/MOMO模拟按键/Photoshop图像处理
- 营业执照、组织机构代码、统一社会信用代码
- 【第152期】游戏策划:给@不五的简历分析
- 另类的切图仔画图方案:svg编辑器+css
- linux C 实现HTTP get 及post 请求
- iWebOffice2009问题
- Unity3D:粒子特效(Particle System)播放序列帧动画