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 当然里面有些写死的东西,需要大家改成自个的。

posted @ 2021-09-15 17:45  javawxid  阅读(92)  评论(0编辑  收藏  举报