前言
最近搞了一套网关校验,路由分发模块,这里分享出来给大家
提示:以下是本篇文章正文内容,下面案例可供参考
一、项目结构
二、步骤
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 { @Autowired private RedisConnectionFactory factory; @Bean public 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; } @Bean public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForHash(); } @Bean public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) { return redisTemplate.opsForValue(); } @Bean public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForList(); } @Bean public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForSet(); } @Bean public 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 { @Autowired private 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; @Autowired private RestTemplate restTemplate; /** * 请求各个微服务 不需要用户认证的URL */ @Autowired private 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; @Override public 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_key this.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中放文件,记得build ServerHttpRequest request = serverWebExchange.getRequest().mutate() // .header("userId",userId) .header("userName",userName) .header("nickName",nickName) // .header("loginType",loginType) .build(); return request; } @Override public 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(); //过滤不需要认证的url if(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 -> { // 根据请求头,用不同的方式解析Body if (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); } } // 加载QueryParameter this.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()) { @Override public 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; } @Override public 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} 请求实体 */ @Override public 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为客户端的真实IP if (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_key try { 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 { @Autowired private 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"; @PostConstruct public 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 @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
bootstrap.yml
server: port: 10002 spring: mvc: async: request-timeout: 20000 static-path-pattern: /** application: # 应用名称 name: gateway main: allow-bean-definition-overriding: true application_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.xxx b8auth: 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: false lowerCaseServiceId: true routes: - id: user uri: lb://user predicates: - Path=/user/** filters: - StripPrefix=1 - id: content uri: lb://xx predicates: - Path=/xx/** filters: - StripPrefix=1 - id: contentPush uri: lb://xx predicates: - Path=/xx/** filters: - StripPrefix=1 redis: database: 0 host: x.xx.xx.xxx port: 8901 # host: xx.xx.xx.xx # port: 38764 password: bU0@rR0\!dE7:*oFdfafsddfs lettuce: pool: min-idle: 8 max-idle: 500 max-active: 2000 max-wait: 10000 timeout: 5000 logging: level: root: info com.gateway: debug environment: uat2 application_key: i`bNdfasfasK0_lJ3{vH0(
总结
以上就是今天要讲的内容,本文仅仅简单介绍了gateway网关的实现,目前当前功能可以拿去直接上线使用,企业级,已实现路由转发,参数校验,配置中心。可以配合授权模块使用:https://liaozhiwei.blog.csdn.net/article/details/120291130 当然里面有些写死的东西,需要大家改成自个的。