zoukankan      html  css  js  c++  java
  • 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 当然里面有些写死的东西,需要大家改成自个的。

  • 相关阅读:
    MySQL主从数据库同步延迟问题解决(转)
    Python2.6升级Python2.7
    Socket网络编程
    Python 面向对象进阶
    Python类基础知识(面向对象基础)
    shell脚本中出现^M
    Centos6下Python3的编译安装
    Python成长之路(常用模块学习)
    SVN使用总结
    xshell锁屏
  • 原文地址:https://www.cnblogs.com/javawxid/p/15644383.html
Copyright © 2011-2022 走看看