Browse Source

网关异常处理

yingp 7 years ago
parent
commit
b8b7e4b206
26 changed files with 506 additions and 239 deletions
  1. 2 2
      base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/api/AccountApi.java
  2. 1 1
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/AccountController.java
  3. 1 1
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/CompanyController.java
  4. 2 0
      base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/AuthApplication.java
  5. 16 7
      base-servers/gateway-server/pom.xml
  6. 0 2
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/GatewayApplication.java
  7. 0 103
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/AccessFilter.java
  8. 25 0
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/AuthFilter.java
  9. 65 0
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/ErrorConfig.java
  10. 0 18
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/SecurityConfig.java
  11. 49 0
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/WebConfig.java
  12. 0 47
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/ZuulConfig.java
  13. 99 0
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/error/MyExceptionHandler.java
  14. 103 39
      base-servers/gateway-server/src/main/resources/application.yml
  15. 2 9
      base-servers/ui-server/pom.xml
  16. 15 10
      base-servers/ui-server/src/main/java/com/usoftchina/saas/ui/controller/co/CoViewController.java
  17. 2 0
      base-servers/ui-server/src/main/resources/application.yml
  18. 1 0
      framework/pom.xml
  19. 40 0
      framework/server-starter/pom.xml
  20. 14 0
      framework/server-starter/src/main/java/com/usoftchina/saas/server/ServerAutoConfiguration.java
  21. 16 0
      framework/server-starter/src/main/java/com/usoftchina/saas/server/context/SpringContextListener.java
  22. 41 0
      framework/server-starter/src/main/java/com/usoftchina/saas/server/error/MyErrorController.java
  23. 7 0
      framework/server-starter/src/main/resources/META-INF/spring.factories
  24. BIN
      framework/server-starter/src/main/resources/auth/pub.key
  25. 0 0
      framework/server-starter/src/main/resources/banner.txt
  26. 5 0
      pom.xml

+ 2 - 2
base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/api/AccountApi.java

@@ -19,7 +19,7 @@ public interface AccountApi {
      * @param password 明文密码
      * @return
      */
-    @GetMapping(value = "/api/account/pwd/check")
+    @GetMapping(value = "/pwd/check")
     Result<AccountDTO> validByUsernameAndPwd(@RequestParam(value = "username") String username, @RequestParam(value = "password") String password);
 
     /**
@@ -28,6 +28,6 @@ public interface AccountApi {
      * @param username
      * @return
      */
-    @GetMapping(value = "/api/account")
+    @GetMapping
     Result<AccountDTO> getAccount(@RequestParam(value = "username") String username);
 }

+ 1 - 1
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/AccountController.java

@@ -21,7 +21,7 @@ import java.util.List;
  * @date 2018/10/2
  */
 @RestController
-@RequestMapping("/api/account")
+@RequestMapping("/account")
 public class AccountController {
 
     @Autowired

+ 1 - 1
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/CompanyController.java

@@ -14,7 +14,7 @@ import org.springframework.web.bind.annotation.*;
  * @date 2018/10/2
  */
 @RestController
-@RequestMapping("/api/account/company")
+@RequestMapping("/company")
 public class CompanyController {
 
     @Autowired

+ 2 - 0
base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/AuthApplication.java

@@ -3,6 +3,7 @@ package com.usoftchina.saas.auth;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
 
 /**
  * @author yingp
@@ -10,6 +11,7 @@ import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
  */
 @SpringBootApplication
 @EnableEurekaClient
+@EnableFeignClients(basePackages = "com.usoftchina.saas.account.api")
 public class AuthApplication {
     public static void main(String[] args) {
         SpringApplication.run(AuthApplication.class, args);

+ 16 - 7
base-servers/gateway-server/pom.xml

@@ -25,34 +25,43 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-actuator</artifactId>
         </dependency>
-        <dependency>
+        <!--<dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-gateway</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>compile</scope>
+            <optional>true</optional>
         </dependency>
-        <!-- zuul -->
         <dependency>
             <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
+            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
         </dependency>
         <!-- rate limit -->
-        <dependency>
+        <!--<dependency>
             <groupId>com.marcosbarbero.cloud</groupId>
             <artifactId>spring-cloud-zuul-ratelimit</artifactId>
             <version>${ratelimit.version}</version>
-        </dependency>
+        </dependency>-->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-redis</artifactId>
         </dependency>
         <!-- sleuth -->
-        <dependency>
+        <!--<dependency>
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-starter-zipkin</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.amqp</groupId>
             <artifactId>spring-rabbit</artifactId>
-        </dependency>
+        </dependency>-->
         <!-- auth -->
         <dependency>
             <groupId>com.usoftchina.saas</groupId>

+ 0 - 2
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/GatewayApplication.java

@@ -3,7 +3,6 @@ package com.usoftchina.saas.gateway;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
-import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
 import org.springframework.cloud.openfeign.EnableFeignClients;
 
 /**
@@ -11,7 +10,6 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
  * @date 2018/9/30
  */
 @SpringBootApplication
-@EnableZuulProxy
 @EnableEurekaClient
 @EnableFeignClients(basePackages = "com.usoftchina.saas.auth.api")
 public class GatewayApplication {

+ 0 - 103
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/AccessFilter.java

@@ -1,103 +0,0 @@
-package com.usoftchina.saas.gateway.config;
-
-import com.netflix.zuul.ZuulFilter;
-import com.netflix.zuul.context.RequestContext;
-import com.netflix.zuul.exception.ZuulException;
-import com.usoftchina.saas.auth.api.AuthApi;
-import com.usoftchina.saas.auth.common.jwt.JwtHelper;
-import com.usoftchina.saas.auth.common.jwt.JwtInfo;
-import com.usoftchina.saas.base.Result;
-import com.usoftchina.saas.exception.BizException;
-import com.usoftchina.saas.exception.ExceptionCode;
-import com.usoftchina.saas.utils.JsonUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.MediaType;
-import org.springframework.util.CollectionUtils;
-import org.springframework.util.StringUtils;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * @author yingp
- * @date 2018/9/30
- */
-public class AccessFilter extends ZuulFilter {
-
-    @Autowired
-    private AuthApi authApi;
-
-    @Autowired
-    private AuthConfig authConfig;
-
-    @Override
-    public String filterType() {
-        return "pre";
-    }
-
-    @Override
-    public int filterOrder() {
-        return 1;
-    }
-
-    @Override
-    public boolean shouldFilter() {
-        return true;
-    }
-
-    /**
-     * 具体过滤逻辑
-     *
-     * @return
-     * @throws ZuulException
-     */
-    @Override
-    public Object run() throws ZuulException {
-        RequestContext ctx = RequestContext.getCurrentContext();
-        HttpServletRequest request = ctx.getRequest();
-        if (isIgnore(request.getRequestURI())) {
-            return null;
-        }
-        String token = request.getHeader(authConfig.getAuthHeader());
-        if (StringUtils.isEmpty(token)) {
-            setFailedRequest(ctx, 401, Result.error(ExceptionCode.JWT_ILLEGAL_ARGUMENT));
-            return null;
-        }
-        try {
-            JwtInfo infoFromToken = JwtHelper.getInfoFromToken(token, authConfig.getPublicKey());
-            // TODO resource + role
-        } catch (BizException e) {
-            setFailedRequest(ctx, 401, Result.error(e));
-            return null;
-        }
-        return null;
-    }
-
-    private boolean isIgnore(String requestUri) {
-        if (!CollectionUtils.isEmpty(authConfig.getIgnores())) {
-            for (String ignore : authConfig.getIgnores()) {
-                if (requestUri.startsWith(ignore)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * 异常请求
-     *
-     * @param ctx
-     * @param code
-     * @param result
-     */
-    public void setFailedRequest(RequestContext ctx, int code, Result result) {
-        ctx.setSendZuulResponse(false);
-        HttpServletResponse httpResponse = ctx.getResponse();
-        httpResponse.setCharacterEncoding("UTF-8");
-        httpResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
-        httpResponse.setStatus(code);
-        ctx.setResponseBody(JsonUtils.toJsonString(result));
-        ctx.setResponse(httpResponse);
-    }
-}

+ 25 - 0
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/AuthFilter.java

@@ -0,0 +1,25 @@
+package com.usoftchina.saas.gateway.config;
+
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.Ordered;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+/**
+ * 全局过滤器鉴权
+ *
+ * @author yingp
+ * @date 2018/10/13
+ */
+public class AuthFilter implements GlobalFilter, Ordered {
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+        return chain.filter(exchange);
+    }
+
+    @Override
+    public int getOrder() {
+        return -100;
+    }
+}

+ 65 - 0
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/ErrorConfig.java

@@ -0,0 +1,65 @@
+package com.usoftchina.saas.gateway.config;
+
+import com.usoftchina.saas.gateway.error.MyExceptionHandler;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.boot.autoconfigure.web.ResourceProperties;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.reactive.error.ErrorAttributes;
+import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.codec.ServerCodecConfigurer;
+import org.springframework.web.reactive.result.view.ViewResolver;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2018/10/13
+ */
+@Configuration
+@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
+public class ErrorConfig {
+
+    private final ServerProperties serverProperties;
+
+    private final ApplicationContext applicationContext;
+
+    private final ResourceProperties resourceProperties;
+
+    private final List<ViewResolver> viewResolvers;
+
+    private final ServerCodecConfigurer serverCodecConfigurer;
+
+    public ErrorConfig(ServerProperties serverProperties,
+                                     ResourceProperties resourceProperties,
+                                     ObjectProvider<List<ViewResolver>> viewResolversProvider,
+                                     ServerCodecConfigurer serverCodecConfigurer,
+                                     ApplicationContext applicationContext) {
+        this.serverProperties = serverProperties;
+        this.applicationContext = applicationContext;
+        this.resourceProperties = resourceProperties;
+        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
+        this.serverCodecConfigurer = serverCodecConfigurer;
+    }
+
+
+    @Bean
+    @Order(Ordered.HIGHEST_PRECEDENCE)
+    public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
+        MyExceptionHandler exceptionHandler = new MyExceptionHandler(
+                errorAttributes,
+                this.resourceProperties,
+                this.serverProperties.getError(),
+                this.applicationContext);
+        exceptionHandler.setViewResolvers(this.viewResolvers);
+        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
+        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
+        return exceptionHandler;
+    }
+}

+ 0 - 18
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/SecurityConfig.java

@@ -1,18 +0,0 @@
-package com.usoftchina.saas.gateway.config;
-
-import org.springframework.security.config.annotation.web.builders.WebSecurity;
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-
-/**
- * @author yingp
- * @date 2018/10/10
- */
-@EnableWebSecurity
-public class SecurityConfig extends WebSecurityConfigurerAdapter {
-
-    @Override
-    public void configure(WebSecurity web) throws Exception {
-        web.ignoring().antMatchers("/api/**");
-    }
-}

+ 49 - 0
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/WebConfig.java

@@ -0,0 +1,49 @@
+package com.usoftchina.saas.gateway.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.web.cors.reactive.CorsUtils;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author yingp
+ * @date 2018/10/13
+ */
+@Configuration
+public class WebConfig {
+
+    @Bean
+    public WebFilter corsFilter() {
+        return (ServerWebExchange ctx, WebFilterChain chain) -> {
+            ServerHttpRequest request = ctx.getRequest();
+            if (!CorsUtils.isCorsRequest(request)) {
+                return chain.filter(ctx);
+            }
+            HttpHeaders requestHeaders = request.getHeaders();
+            ServerHttpResponse response = ctx.getResponse();
+            HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
+            HttpHeaders headers = response.getHeaders();
+            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
+            headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());
+            if (requestMethod != null) {
+                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
+            }
+            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
+            headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
+            headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "18000L");
+            if (request.getMethod() == HttpMethod.OPTIONS) {
+                response.setStatusCode(HttpStatus.OK);
+                return Mono.empty();
+            }
+            return chain.filter(ctx);
+        };
+    }
+}

+ 0 - 47
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/ZuulConfig.java

@@ -1,47 +0,0 @@
-package com.usoftchina.saas.gateway.config;
-
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.cors.CorsConfiguration;
-import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
-import org.springframework.web.filter.CorsFilter;
-
-/**
- * @author yingp
- * @date 2018/9/30
- */
-@Configuration
-@EnableConfigurationProperties(AuthConfig.class)
-public class ZuulConfig {
-
-    @Bean
-    public CorsFilter corsFilter() {
-        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
-        CorsConfiguration config = new CorsConfiguration();
-        // 允许cookies跨域
-        config.setAllowCredentials(true);
-        // 允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
-        config.addAllowedOrigin("*");
-        // 允许访问的头信息,*表示全部
-        config.addAllowedHeader("*");
-        // 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
-        config.setMaxAge(18000L);
-        // 允许提交请求的方法,*表示全部允许
-        config.addAllowedMethod("OPTIONS");
-        config.addAllowedMethod("HEAD");
-        // 允许Get的请求方法
-        config.addAllowedMethod("GET");
-        config.addAllowedMethod("PUT");
-        config.addAllowedMethod("POST");
-        config.addAllowedMethod("DELETE");
-        config.addAllowedMethod("PATCH");
-        source.registerCorsConfiguration("/**", config);
-        return new CorsFilter(source);
-    }
-
-    @Bean
-    public AccessFilter accessFilter() {
-        return new AccessFilter();
-    }
-}

+ 99 - 0
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/error/MyExceptionHandler.java

@@ -0,0 +1,99 @@
+package com.usoftchina.saas.gateway.error;
+
+import org.springframework.boot.autoconfigure.web.ErrorProperties;
+import org.springframework.boot.autoconfigure.web.ResourceProperties;
+import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
+import org.springframework.boot.web.reactive.error.ErrorAttributes;
+import org.springframework.cloud.gateway.support.NotFoundException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.reactive.function.server.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 自定义异常处理
+ *
+ * @author yingp
+ * @date 2018/10/13
+ */
+public class MyExceptionHandler extends DefaultErrorWebExceptionHandler {
+
+    public MyExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
+                              ErrorProperties errorProperties, ApplicationContext applicationContext) {
+        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
+    }
+
+    /**
+     * 获取异常属性
+     */
+    @Override
+    protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
+        int code = 500;
+        Throwable error = super.getError(request);
+        if (error instanceof NotFoundException) {
+            code = 404;
+        }
+        return response(code, this.buildMessage(request, error));
+    }
+
+    /**
+     * 构建返回的JSON数据格式
+     *
+     * @param status  状态码
+     * @param message 异常信息
+     * @return
+     */
+    private Map<String, Object> response(int status, String message) {
+        Map<String, Object> map = new HashMap<>(4);
+        map.put("success", false);
+        map.put("code", status);
+        map.put("message", message);
+        map.put("data", null);
+        return map;
+
+    }
+
+    /**
+     * 构建异常信息
+     *
+     * @param request
+     * @param ex
+     * @return
+     */
+    private String buildMessage(ServerRequest request, Throwable ex) {
+        StringBuilder message = new StringBuilder("Failed to handle request [");
+        message.append(request.methodName());
+        message.append(" ");
+        message.append(request.uri());
+        message.append("]");
+        if (ex != null) {
+            message.append(": ");
+            message.append(ex.getMessage());
+        }
+        return message.toString();
+    }
+
+    /**
+     * 根据code获取对应的HttpStatus
+     *
+     * @param errorAttributes
+     */
+    @Override
+    protected HttpStatus getHttpStatus(Map<String, Object> errorAttributes) {
+        int statusCode = (int) errorAttributes.get("code");
+        return HttpStatus.valueOf(statusCode);
+    }
+
+    /**
+     * 指定响应处理方法为JSON处理的方法
+     *
+     * @param errorAttributes
+     */
+    @Override
+    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
+        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
+    }
+
+}

+ 103 - 39
base-servers/gateway-server/src/main/resources/application.yml

@@ -17,9 +17,87 @@ spring:
   zipkin:
     sender:
       type: rabbit
+    service:
+      name: ${spring.application.name}
   sleuth:
     sampler:
-      percentage: 1.0
+      percentage: 1
+    integration:
+      enabled: false
+    scheduled:
+      skip-pattern: "^org.*HystrixStreamTask$"
+  cloud:
+    gateway:
+      discovery:
+        locator:
+          enabled: true
+      routes:
+      - id: UI-SERVER
+        uri: lb://UI-SERVER
+        predicates:
+        - Path=/api/ui/**
+        filters:
+        - RewritePath=/api/ui/(?<segment>.*), /$\{segment}
+      - id: ACCOUNT-SERVER
+        uri: lb://ACCOUNT-SERVER
+        predicates:
+        - Path=/api/account/**
+        filters:
+        - RewritePath=/api/account/(?<segment>.*), /$\{segment}
+      - id: AUTH-SERVER
+        uri: lb://AUTH-SERVER
+        predicates:
+        - Path=/api/auth/**
+        filters:
+        - RewritePath=/api/auth/(?<segment>.*), /$\{segment}
+      - id: FILE-SERVER
+        uri: lb://FILE-SERVER
+        predicates:
+        - Path=/api/file/**
+        filters:
+        - RewritePath=/api/file/(?<segment>.*), /$\{segment}
+      - id: MAIL-SERVER
+        uri: lb://MAIL-SERVER
+        predicates:
+        - Path=/api/mail/**
+        filters:
+        - RewritePath=/api/mail/(?<segment>.*), /$\{segment}
+      - id: SMS-SERVER
+        uri: lb://SMS-SERVER
+        predicates:
+        - Path=/api/sms/**
+        filters:
+        - RewritePath=/api/sms/(?<segment>.*), /$\{segment}
+      - id: PURCHASE-SERVER
+        uri: lb://PURCHASE-SERVER
+        predicates:
+        - Path=/api/purchase/**
+        filters:
+        - RewritePath=/api/purchase/(?<segment>.*), /$\{segment}
+      - id: SALE-SERVER
+        uri: lb://SALE-SERVER
+        predicates:
+        - Path=/api/sale/**
+        filters:
+        - RewritePath=/api/sale/(?<segment>.*), /$\{segment}
+      - id: STORAGE-SERVER
+        uri: lb://STORAGE-SERVER
+        predicates:
+        - Path=/api/storage/**
+        filters:
+        - RewritePath=/api/storage/(?<segment>.*), /$\{segment}
+      - id: DOCUMENT-SERVER
+        uri: lb://DOCUMENT-SERVER
+        predicates:
+        - Path=/api/document/**
+        filters:
+        - RewritePath=/api/document/(?<segment>.*), /$\{segment}
+      - id: MONEY-SERVER
+        uri: lb://MONEY-SERVER
+        predicates:
+        - Path=/api/money/**
+        filters:
+        - RewritePath=/api/money/(?<segment>.*), /$\{segment}
 server:
   port: 8560
   tomcat:
@@ -38,6 +116,8 @@ eureka:
     serviceUrl:
       defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@192.168.0.181:8500/eureka/
 feign:
+  hystrix:
+    enabled: true
   compression:
     request:
       enabled: true
@@ -45,44 +125,21 @@ feign:
       min-request-size: 2048
     response:
       enabled: true
-zuul:
-  ignored-services: "*"   # 忽略eureka上的所有服务
-  sensitive-headers:      # 一些比较敏感的请求头,不想通过zuul传递过去, 可以通过该属性进行设置
-  prefix: /api            # 公共的前缀
-  routes:  # 路由配置
-    ui:
-      path: /ui/**
-      serviceId: ui-server
-    account:
-      path: /account/**
-      serviceId: account-server
-    auth:
-      path: /auth/**
-      serviceId: auth-server
-    file:
-      path: /file/**
-      serviceId: file-server
-    mail:
-      path: /mail/**
-      serviceId: mail-server
-    sms:
-      path: /sms/**
-      serviceId: sms-server
-    purchase:
-      path: /purchase/**
-      serviceId: purchase-server
-    sale:
-      path: /sale/**
-      serviceId: sale-server
-    storage:
-      path: /storage/**
-      serviceId: storage-server
-    document:
-      path: /document/**
-      serviceId: document-server
-    money:
-      path: /money/**
-      serviceId: money-server                       
+hystrix:
+  command:
+    default:
+      execution:
+        isolation:
+          thread:
+            timeoutInMilliseconds: 3000
+ribbon:
+  eureka:
+    enabled: true
+  ReadTimeout: 60000
+  ConnectTimeout: 60000
+  MaxAutoRetries: 0
+  MaxAutoRetriesNextServer: 1
+  OkToRetryOnAllOperations: false
 info:
   name: '@project.artifactId@'
   description: '@project.description@'
@@ -95,3 +152,10 @@ auth:
     - /api/auth
     # 忽略全部
     - /
+logging:
+  level:
+    org.springframework.cloud.gateway: debug
+    org.springframework.http.server.reactive: debug
+    org.springframework.web.reactive: debug
+    org.springframework.security: debug
+    reactor.ipc.netty: debug

+ 2 - 9
base-servers/ui-server/pom.xml

@@ -15,20 +15,12 @@
     <dependencies>
         <dependency>
             <groupId>com.usoftchina.saas</groupId>
-            <artifactId>core</artifactId>
+            <artifactId>server-starter</artifactId>
         </dependency>
         <dependency>
             <groupId>com.usoftchina.saas</groupId>
             <artifactId>auth-client</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.springframework.cloud</groupId>
-            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-actuator</artifactId>
-        </dependency>
         <!-- db -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
@@ -47,6 +39,7 @@
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
         </dependency>
     </dependencies>
     <build>

+ 15 - 10
base-servers/ui-server/src/main/java/com/usoftchina/saas/ui/controller/co/CoViewController.java

@@ -1,6 +1,7 @@
 package com.usoftchina.saas.ui.controller.co;
 
 import com.usoftchina.saas.base.Result;
+import com.usoftchina.saas.context.BaseContextHolder;
 import com.usoftchina.saas.ui.service.ViewService;
 import com.usoftchina.saas.ui.service.co.CoViewService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -26,20 +27,24 @@ public class CoViewController {
      * 获取企业视图配置
      *
      * @param name
-     * @param refresh 是否强制刷新
+     * @param refresh    是否强制刷新
+     * @param useDefault 当自定义配置为空时,是否返回标准配置
      * @return
      */
     @GetMapping("/config")
-    public Result<Object> getViewConfig(@RequestParam String name, Boolean refresh) {
-//        if (null != refresh && refresh) {
-//            coViewService.cacheEvict(name);
-//            viewService.cacheEvict(name);
-//        }
-        coViewService.cacheEvict(name);// 暂时强制刷新
-        viewService.cacheEvict(name);
-
+    public Result<Object> getViewConfig(@RequestParam String name,
+                                        @RequestParam(required = false, defaultValue = "true") Boolean refresh,
+                                        @RequestParam(required = false, defaultValue = "true") Boolean useDefault) {
+        if (null != refresh && refresh) {
+            coViewService.cacheEvict(name);
+            viewService.cacheEvict(name);
+        }
+        // TODO 开发默认设置,及时去掉
+        if (null == BaseContextHolder.getCompanyId()) {
+            BaseContextHolder.setCompanyId(0);
+        }
         Object config = coViewService.getDeepConfig(name);
-        if (null == config) {
+        if (null == config && null != useDefault && useDefault) {
             // 企业配置不存在则取标准配置
             config = viewService.getDeepConfig(name);
         }

+ 2 - 0
base-servers/ui-server/src/main/resources/application.yml

@@ -13,6 +13,8 @@ spring:
   zipkin:
     sender:
       type: rabbit
+    service:
+      name: ${spring.application.name}
   sleuth:
     sampler:
       percentage: 1.0

+ 1 - 0
framework/pom.xml

@@ -14,6 +14,7 @@
     <description>saas framework</description>
     <modules>
         <module>core</module>
+        <module>server-starter</module>
     </modules>
 
 </project>

+ 40 - 0
framework/server-starter/pom.xml

@@ -0,0 +1,40 @@
+<?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">
+    <parent>
+        <artifactId>framework</artifactId>
+        <groupId>com.usoftchina.saas</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>server-starter</artifactId>
+    <description>server starter</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>compile</scope>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+
+</project>

+ 14 - 0
framework/server-starter/src/main/java/com/usoftchina/saas/server/ServerAutoConfiguration.java

@@ -0,0 +1,14 @@
+package com.usoftchina.saas.server;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author yingp
+ * @date 2018/10/13
+ */
+@Configuration
+@ComponentScan(basePackages = {"com.usoftchina.saas.server"})
+public class ServerAutoConfiguration {
+    
+}

+ 16 - 0
framework/server-starter/src/main/java/com/usoftchina/saas/server/context/SpringContextListener.java

@@ -0,0 +1,16 @@
+package com.usoftchina.saas.server.context;
+
+import com.usoftchina.saas.context.SpringContextHolder;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+
+/**
+ * @author yingp
+ * @date 2018/7/18
+ */
+public class SpringContextListener implements ApplicationListener<ContextRefreshedEvent> {
+    @Override
+    public void onApplicationEvent(ContextRefreshedEvent event) {
+        SpringContextHolder.setContext(event.getApplicationContext());
+    }
+}

+ 41 - 0
framework/server-starter/src/main/java/com/usoftchina/saas/server/error/MyErrorController.java

@@ -0,0 +1,41 @@
+package com.usoftchina.saas.server.error;
+
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
+import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author yingp
+ * @date 2018/10/13
+ */
+@Controller
+public class MyErrorController extends BasicErrorController {
+
+    public MyErrorController(ServerProperties serverProperties) {
+        super(new DefaultErrorAttributes(), serverProperties.getError());
+    }
+
+    /**
+     * 覆盖默认的错误响应
+     */
+    @Override
+    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
+        Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
+        HttpStatus status = getStatus(request);
+        // 输出自定义格式
+        Map<String, Object> map = new HashMap<>(4);
+        map.put("success", false);
+        map.put("code", body.get("status"));
+        map.put("message", body.get("message"));
+        map.put("data", null);
+        return new ResponseEntity<>(map, status);
+    }
+}

+ 7 - 0
framework/server-starter/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,7 @@
+# Auto Configuration
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.usoftchina.saas.server.ServerAutoConfiguration
+
+# Application Listeners
+org.springframework.context.ApplicationListener=\
+com.usoftchina.saas.server.context.SpringContextListener

BIN
framework/server-starter/src/main/resources/auth/pub.key


+ 0 - 0
base-servers/ui-server/src/main/resources/banner.txt → framework/server-starter/src/main/resources/banner.txt


+ 5 - 0
pom.xml

@@ -190,6 +190,11 @@
                 <artifactId>core</artifactId>
                 <version>${project.release.version}</version>
             </dependency>
+            <dependency>
+                <groupId>com.usoftchina.saas</groupId>
+                <artifactId>server-starter</artifactId>
+                <version>${project.release.version}</version>
+            </dependency>
             <dependency>
                 <groupId>com.usoftchina.saas</groupId>
                 <artifactId>file-dto</artifactId>