Demo:第四章:Gateway网关
前言
最近搞了一套网关校验,路由分发模块,这里分享出来给大家
提示:以下是本篇文章正文内容,下面案例可供参考
一、项目结构
二、步骤
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 当然里面有些写死的东西,需要大家改成自个的。