Ver código fonte

UAT环境提交

houweiyu 4 anos atrás
commit
58d85a2e49

+ 201 - 0
pom.xml

@@ -0,0 +1,201 @@
+<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.1.4.RELEASE</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+
+    <packaging>jar</packaging>
+
+    <groupId>com.fcb.gateway</groupId>
+    <artifactId>gateway</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>gateway</name>
+    <description>房车宝网关</description>
+
+    <properties>
+        <java.version>1.8</java.version>
+        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
+        <alibaba.version>0.9.0.RELEASE</alibaba.version>
+        <nacos-config-spring-boot.version>0.2.1</nacos-config-spring-boot.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
+                <version>0.9.0.RELEASE</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>${spring-cloud.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+
+                <exclusion>
+                    <groupId>ch.qos.logback</groupId>
+                    <artifactId>logback-classic</artifactId>
+                </exclusion>
+
+                <exclusion>
+                    <groupId>ch.qos.logback</groupId>
+                    <artifactId>logback-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-gateway</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-log4j2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>redis.clients</groupId>
+            <artifactId>jedis</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.google.guava</groupId>
+                    <artifactId>guava</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- nacos 注册中心依赖 -->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.google.guava</groupId>
+                    <artifactId>guava</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!-- nacos 配置中心依赖 -->
+        <dependency>
+            <groupId>com.alibaba.boot</groupId>
+            <artifactId>nacos-config-spring-boot-starter</artifactId>
+            <version>${nacos-config-spring-boot.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.google.guava</groupId>
+                    <artifactId>guava</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.google.guava</groupId>
+                    <artifactId>guava</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.google.guava</groupId>
+                    <artifactId>guava</artifactId>
+                </exclusion>
+
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+
+                <exclusion>
+                    <groupId>ch.qos.logback</groupId>
+                    <artifactId>logback-classic</artifactId>
+                </exclusion>
+
+                <exclusion>
+                    <groupId>ch.qos.logback</groupId>
+                    <artifactId>logback-core</artifactId>
+                </exclusion>
+
+            </exclusions>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-alibaba-nacos-config</artifactId>
+            <version>0.9.0.RELEASE</version>
+        </dependency>
+    </dependencies>
+
+
+
+    <build>
+        <finalName>fcb_gateway_01</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <!-- 工程主入口 -->
+                    <mainClass>com.fcb.gateway.GatewayApplication</mainClass>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 18 - 0
src/main/java/com/fcb/gateway/GatewayApplication.java

@@ -0,0 +1,18 @@
+package com.fcb.gateway;
+
+import com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+
+@SpringBootApplication(scanBasePackages = {"com.fcb.gateway"})
+@EnableDiscoveryClient
+//@NacosPropertySource(dataId = "custom-routers", autoRefreshed = true)
+public class GatewayApplication {
+
+
+    public static void main(String[] args) {
+        SpringApplication.run(GatewayApplication.class, args);
+    }
+
+}

+ 15 - 0
src/main/java/com/fcb/gateway/config/RateLimiterConfiguration.java

@@ -0,0 +1,15 @@
+package com.fcb.gateway.config;
+
+import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import reactor.core.publisher.Mono;
+
+@Configuration
+public class RateLimiterConfiguration {
+
+    @Bean(value = "ipKeyResolver")
+    public KeyResolver ipKeyResolver() {
+        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
+    }
+}

+ 65 - 0
src/main/java/com/fcb/gateway/config/RedisConfiguration.java

@@ -0,0 +1,65 @@
+package com.fcb.gateway.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.*;
+import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+@Configuration
+public class RedisConfiguration {
+
+    @Bean
+    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
+        RedisTemplate<String, Object> template = new RedisTemplate<>();
+        template.setConnectionFactory(factory);
+
+        // 使用Jackson2JsonRedisSerialize 替换默认的jdkSerializeable序列化
+        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
+        ObjectMapper om = new ObjectMapper();
+        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        jackson2JsonRedisSerializer.setObjectMapper(om);
+
+        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
+
+        // key采用String的序列化方式
+        template.setKeySerializer(stringRedisSerializer);
+        // hash的key也采用String的序列化方式
+        template.setHashKeySerializer(stringRedisSerializer);
+        // value序列化方式采用jackson
+        template.setValueSerializer(jackson2JsonRedisSerializer);
+        // hash的value序列化方式采用jackson
+        template.setHashValueSerializer(jackson2JsonRedisSerializer);
+        template.afterPropertiesSet();
+        return template;
+    }
+
+    /*@Bean("redisTemplate")
+    public RedisTemplate redisTemplate(@Value("${spring.redis.host}") String host,
+                                       @Value("${spring.redis.port}") int port) {
+        RedisTemplate redisTemplate = new RedisTemplate();
+        RedisSerializer stringRedisSerializer = new StringRedisSerializer();
+        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
+        redisTemplate.setKeySerializer(stringRedisSerializer);
+        redisTemplate.setHashKeySerializer(stringRedisSerializer);
+        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
+        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
+        redisTemplate.setConnectionFactory(standaloneConnectionFactory(host, port));
+        return redisTemplate;
+    }
+
+    protected JedisConnectionFactory standaloneConnectionFactory(String host, int port) {
+        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
+        redisStandaloneConfiguration.setHostName(host);
+        redisStandaloneConfiguration.setPort(port);
+        return new JedisConnectionFactory(redisStandaloneConfiguration);
+    }*/
+}

+ 19 - 0
src/main/java/com/fcb/gateway/controller/FallbackController.java

@@ -0,0 +1,19 @@
+package com.fcb.gateway.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@RestController
+public class FallbackController {
+
+    @RequestMapping("/defaultFallback")
+    public Map defaultFallback() {
+        Map map = new HashMap<>();
+        map.put("code", 1);
+        map.put("message", "服务异常");
+        return map;
+    }
+}

+ 165 - 0
src/main/java/com/fcb/gateway/filter/AuthAndLogFilter.java

@@ -0,0 +1,165 @@
+package com.fcb.gateway.filter;
+
+import com.alibaba.fastjson.JSON;
+import com.fcb.gateway.util.MD5Utils;
+import lombok.extern.log4j.Log4j2;
+import org.apache.commons.lang3.StringUtils;
+import org.reactivestreams.Publisher;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.Ordered;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.DataBufferFactory;
+import org.springframework.core.io.buffer.DataBufferUtils;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+@Component
+@Log4j2
+public class AuthAndLogFilter implements GlobalFilter, Ordered {
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+
+        ServerHttpRequest serverHttpRequest = exchange.getRequest();
+        ServerHttpResponse serverHttpResponse = exchange.getResponse();
+        //TODO:这个后面要将日志级别改成debug
+        log.info("==========网关收到请求:{}==========", serverHttpRequest.getPath());
+        StringBuilder logBuilder = new StringBuilder();
+        List<String> tokenList = serverHttpRequest.getHeaders().get("token");
+        Map<String, Object> mapResult = new HashMap<>();
+        String requestUrl = exchange.getRequest().getPath().toString();
+
+        if(requestUrl.startsWith("/api/scene/camera/findBySnCode")
+                || requestUrl.startsWith("/api/manage/department/findById")
+                || requestUrl.startsWith("/api/manage/user/login")
+                || requestUrl.startsWith("/api/scene/app/getSceneStatusByUnicode")
+                || requestUrl.startsWith("/api/scene/app/getScreencapVoice")
+                || requestUrl.startsWith("/api/scene/getInfo")
+        ){
+            log.info("白名单路径,无需校验token");
+        }else{
+            if (!CollectionUtils.isEmpty(tokenList)) {
+                String token = tokenList.get(0);
+                if(!redisTemplate.hasKey(token)){
+                    log.info("登录超时,需要重新登录");
+                    mapResult.put("code", 500);
+                    mapResult.put("message", "token已经失效,请重新登录");
+                    DataBuffer bodyDataBuffer = serverHttpResponse.bufferFactory().wrap(JSON.toJSONString(mapResult).getBytes());
+                    serverHttpResponse.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
+                    return serverHttpResponse.writeWith(Mono.just(bodyDataBuffer));
+                }
+            }else{
+                log.info("没有上送token");
+                mapResult.put("code", 500);
+                mapResult.put("message", "token缺失");
+                DataBuffer bodyDataBuffer = serverHttpResponse.bufferFactory().wrap(JSON.toJSONString(mapResult).getBytes());
+                serverHttpResponse.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
+                return serverHttpResponse.writeWith(Mono.just(bodyDataBuffer));
+            }
+        }
+        DataBufferFactory bufferFactory = serverHttpResponse.bufferFactory();
+        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(serverHttpResponse) {
+            @Override
+            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
+                if (body instanceof Flux) {
+                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
+                    return super.writeWith(fluxBody.map(dataBuffer -> {
+                        byte[] content = new byte[dataBuffer.readableByteCount()];
+                        dataBuffer.read(content);
+                        DataBufferUtils.release(dataBuffer);
+                        String resp = new String(content, Charset.forName("UTF-8"));
+                        logBuilder.append(",resp=").append(resp);
+                        log.info(logBuilder.toString());
+                        byte[] uppedContent = new String(content, Charset.forName("UTF-8")).getBytes();
+                        return bufferFactory.wrap(uppedContent);
+                    }));
+                }
+                return super.writeWith(body);
+            }
+        };
+        return chain.filter(exchange.mutate().response(decoratedResponse).build());
+    }
+
+    private Map<String, String> parseRequest(ServerWebExchange exchange, StringBuilder logBuilder) {
+        ServerHttpRequest serverHttpRequest = exchange.getRequest();
+        String method = serverHttpRequest.getMethodValue().toUpperCase();
+        logBuilder.append(method).append(",").append(serverHttpRequest.getURI());
+        MultiValueMap<String, String> query = serverHttpRequest.getQueryParams();
+        Map<String, String> params = new HashMap<>();
+        query.forEach((k, v) -> {
+            params.put(k, v.get(0));
+        });
+        if ("POST".equals(method)) {
+            String body = exchange.getAttributeOrDefault("cachedRequestBody", "");
+            if (StringUtils.isNotBlank(body)) {
+                logBuilder.append(",body=").append(body);
+                String[] kvArray = body.split("&");
+                for (String kv : kvArray) {
+                    if (kv.indexOf("=") >= 0) {
+                        String k = kv.split("=")[0];
+                        String v = kv.split("=")[1];
+                        if (!params.containsKey(k)) {
+                            try {
+                                params.put(k, URLDecoder.decode(v, "UTF-8"));
+                            } catch (UnsupportedEncodingException e) {
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return params;
+    }
+
+    private boolean checkSignature(Map<String, String> params, ServerHttpRequest serverHttpRequest) {
+
+        String sign = params.get("sign");
+        if (StringUtils.isBlank(sign)) {
+            return false;
+        }
+        //检查签名
+        Map<String, String> sorted = new TreeMap<>();
+        params.forEach((k, v) -> {
+            if (!"sign".equals(k)) {
+                sorted.put(k, v);
+            }
+        });
+        StringBuilder builder = new StringBuilder();
+        sorted.forEach((k, v) -> {
+            builder.append(k).append("=").append(v).append("&");
+        });
+        String value = builder.toString();
+        value = value.substring(0, value.length() - 1);
+        if (!sign.equalsIgnoreCase(MD5Utils.MD5(value))) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int getOrder() {
+        return -20;
+    }
+}

+ 61 - 0
src/main/java/com/fcb/gateway/filter/CachePostBodyFilter.java

@@ -0,0 +1,61 @@
+package com.fcb.gateway.filter;
+
+import io.netty.buffer.UnpooledByteBufAllocator;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.cloud.gateway.support.DefaultServerRequest;
+import org.springframework.core.Ordered;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.NettyDataBufferFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+@Component
+@Log4j2
+public class CachePostBodyFilter implements GlobalFilter, Ordered {
+
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        ServerHttpRequest serverHttpRequest = exchange.getRequest();
+        String method = serverHttpRequest.getMethodValue();
+        log.info("收到请求:{}", serverHttpRequest.getURI());
+        log.info("请求的参数为: {}", serverHttpRequest.getQueryParams());
+        if ("POST".equalsIgnoreCase(method)) {
+            ServerRequest serverRequest = new DefaultServerRequest(exchange);
+            Mono<String> bodyToMono = serverRequest.bodyToMono(String.class);
+            return bodyToMono.flatMap(body -> {
+                exchange.getAttributes().put("cachedRequestBody", body);
+                ServerHttpRequest newRequest = new ServerHttpRequestDecorator(serverHttpRequest) {
+                    @Override
+                    public HttpHeaders getHeaders() {
+                        HttpHeaders httpHeaders = new HttpHeaders();
+                        httpHeaders.putAll(super.getHeaders());
+                        httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
+                        return httpHeaders;
+                    }
+
+                    @Override
+                    public Flux<DataBuffer> getBody() {
+                        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false));
+                        DataBuffer bodyDataBuffer = nettyDataBufferFactory.wrap(body.getBytes());
+                        return Flux.just(bodyDataBuffer);
+                    }
+                };
+                return chain.filter(exchange.mutate().request(newRequest).build());
+            });
+        }
+        return chain.filter(exchange);
+    }
+
+    @Override
+    public int getOrder() {
+        return -21;
+    }
+}

+ 96 - 0
src/main/java/com/fcb/gateway/route/NacosDynamicRouteService.java

@@ -0,0 +1,96 @@
+package com.fcb.gateway.route;
+
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.nacos.api.NacosFactory;
+import com.alibaba.nacos.api.config.ConfigService;
+import com.alibaba.nacos.api.config.annotation.NacosValue;
+import com.alibaba.nacos.api.config.listener.Listener;
+import com.alibaba.nacos.api.exception.NacosException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
+import org.springframework.cloud.gateway.route.RouteDefinition;
+import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
+import org.springframework.stereotype.Component;
+import reactor.core.publisher.Mono;
+
+import javax.annotation.PostConstruct;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+@Component
+public class NacosDynamicRouteService implements ApplicationEventPublisherAware {
+
+    private String dataId = "gateway-router";
+
+    private String group = "DEFAULT_GROUP";
+
+    @Value("${spring.cloud.nacos.config.server-addr}")
+    private String serverAddr;
+
+
+    @Autowired
+    private RouteDefinitionWriter routeDefinitionWriter;
+
+    private ApplicationEventPublisher applicationEventPublisher;
+
+    private static final List<String> ROUTE_LIST = new ArrayList<>();
+
+    @PostConstruct
+    public void dynamicRouteByNacosListener() {
+        try {
+            ConfigService configService = NacosFactory.createConfigService(serverAddr);
+            configService.getConfig(dataId, group, 5000);
+            configService.addListener(dataId, group, new Listener() {
+                @Override
+                public void receiveConfigInfo(String configInfo) {
+                    clearRoute();
+                    try {
+                        List<RouteDefinition> gatewayRouteDefinitions = JSONObject.parseArray(configInfo, RouteDefinition.class);
+                        for (RouteDefinition routeDefinition : gatewayRouteDefinitions) {
+                            addRoute(routeDefinition);
+                        }
+                        publish();
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }
+
+                @Override
+                public Executor getExecutor() {
+                    return null;
+                }
+            });
+        } catch (NacosException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void clearRoute() {
+        for(String id : ROUTE_LIST) {
+            this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
+        }
+        ROUTE_LIST.clear();
+    }
+
+    private void addRoute(RouteDefinition definition) {
+        try {
+            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
+            ROUTE_LIST.add(definition.getId());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void publish() {
+        this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
+    }
+
+    @Override
+    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
+        this.applicationEventPublisher = applicationEventPublisher;
+    }
+}

+ 37 - 0
src/main/java/com/fcb/gateway/util/MD5Utils.java

@@ -0,0 +1,37 @@
+package com.fcb.gateway.util;
+
+import java.security.MessageDigest;
+
+/**
+ * MD5算法加密
+ */
+public abstract class MD5Utils {
+    private static final String MD5 = "MD5";
+    private static final String DEFAULT_ENCODING = "UTF-8";
+
+    public final static String MD5(String source, String encoding) {
+        char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+        try {
+            byte[] btInput = source.getBytes(encoding);
+            MessageDigest mdInst = MessageDigest.getInstance(MD5);
+            mdInst.update(btInput);
+            byte[] md = mdInst.digest();
+            int j = md.length;
+            char str[] = new char[j * 2];
+            int k = 0;
+            for (int i = 0; i < j; i++) {
+                byte byte0 = md[i];
+                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
+                str[k++] = hexDigits[byte0 & 0xf];
+            }
+            return new String(str);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public final static String MD5(String source) {
+        return MD5(source, DEFAULT_ENCODING);
+    }
+}

+ 18 - 0
src/main/resources/application.yml

@@ -0,0 +1,18 @@
+server:
+  port: 8084
+spring:
+  redis:
+    cluster:
+      max-redirects: 3
+      nodes: 10.71.184.237:7000,10.71.184.181:7000,10.71.184.217:7000
+    timeout: 5000ms
+    jedis:
+      pool:
+        max-active: 8
+        max-idle: 8
+        max-wait: -1
+        min-idle: 0
+    password: QBzhHTxfcYLTs0n5J5VQ
+    database: 0
+logging:
+  config: classpath:log4j2-spring.xml

+ 27 - 0
src/main/resources/bootstrap.yml

@@ -0,0 +1,27 @@
+spring:
+  application:
+    name: gateway
+  cloud:
+    nacos:
+      config:
+        server-addr: 10.71.5.163:80
+      discovery:
+        server-addr: 10.71.5.163:80
+    gateway:
+      discovery:
+        locator:
+          enabled: true
+      default-filters:
+      - name: Hystrix
+        args:
+          name : default
+          fallbackUri: 'forward:/defaultFallback'
+
+hystrix:
+  command:
+    default:
+      execution:
+        isolation:
+          strategy: SEMAPHORE
+          thread:
+            timeoutInMilliseconds: 2000

+ 51 - 0
src/main/resources/log4j2-spring.xml

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="WARN" monitorInterval="1800">
+    <properties>
+        <property name="LOG_HOME">/var/logs/fcb-gateway</property>
+        <property name="REQUEST_FILE_NAME">request</property>
+        <property name="INFO_FILE_NAME">gateway-info</property>
+    </properties>
+
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT">
+            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
+        </Console>
+
+        <RollingRandomAccessFile name="info-log"
+                                 fileName="${LOG_HOME}/${INFO_FILE_NAME}.log"
+                                 filePattern="${LOG_HOME}/$${date:yyyy-MM}/${INFO_FILE_NAME}-%d{yyyy-MM-dd}-%i.log">
+            <PatternLayout
+                    pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %level [%thread][%file:%line] - %msg%n"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy/>
+                <SizeBasedTriggeringPolicy size="200 MB"/>
+            </Policies>
+            <DefaultRolloverStrategy max="200"/>
+        </RollingRandomAccessFile>
+
+        <RollingRandomAccessFile name="request-log"
+                                 fileName="${LOG_HOME}/${REQUEST_FILE_NAME}.log"
+                                 filePattern="${LOG_HOME}/$${date:yyyy-MM}/${REQUEST_FILE_NAME}-%d{yyyy-MM-dd}-%i.log">
+            <PatternLayout
+                    pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %level [%thread][%file:%line] - %msg%n"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy/>
+                <SizeBasedTriggeringPolicy size="200 MB"/>
+            </Policies>
+            <DefaultRolloverStrategy max="200"/>
+        </RollingRandomAccessFile>
+    </Appenders>
+
+    <Loggers>
+        <Root level="info">
+            <AppenderRef ref="info-log" />
+        </Root>
+        <Logger name="request" level="info"
+                additivity="false">
+            <AppenderRef ref="request-log"/>
+        </Logger>
+        <Logger name="org.springframework">
+            <AppenderRef ref="Console" />
+        </Logger>
+    </Loggers>
+</Configuration>

+ 16 - 0
src/test/java/com/fcb/gateway/GatewayApplicationTests.java

@@ -0,0 +1,16 @@
+package com.fcb.gateway;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class GatewayApplicationTests {
+
+    @Test
+    public void contextLoads() {
+    }
+
+}