Springcloud之gateway配置及swagger集成

摘要:
前言关于引入gateway的好处我网上找了下:性能:API高可用,负载均衡,容错机制。
前言

关于引入gateway的好处我网上找了下:

  • 性能:API高可用,负载均衡,容错机制。
  • 安全:权限身份认证、脱敏,流量清洗,后端签名(保证全链路可信调用),黑名单(非法调用的限制)。
  • 日志:日志记录(spainid,traceid)一旦涉及分布式,全链路跟踪必不可少。
  • 缓存:数据缓存。监控:记录请求响应数据,api耗时分析,性能监控。
  • 限流:流量控制,错峰流控,可以定义多种限流规则。
  • 灰度:线上灰度部署,可以减小风险。
  • 路由:动态路由规则。
配置

1.png

依赖

compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
compile('org.springframework.cloud:spring-cloud-starter-gateway')
compile("org.springframework.cloud:spring-cloud-starter-openfeign")
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"

application加注解

@EnableFeignClients
@EnableDiscoveryClient

yml

ribbon:
  ConnectTimeout: 60000
  ReadTimeout: 60000
  eureka:
    enabled: true

spring:
  profiles:
    active: dev
  application:
    name: web-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: false
          lower-case-service-id: true
      routes:
        - id: pc-api
          uri: lb://pc-api
          order: -1
          predicates:
            - Path=/api/pc/**
          filters:
            - StripPrefix=2
            - SwaggerHeaderFilter
        - id: admin-api
          uri: lb://admin-api
          order: -1
          predicates:
            - Path=/api/admin/**
          filters:
            - StripPrefix=2
            - SwaggerHeaderFilter #swagger过滤器
            - AdminAuthFilter=true #管理后台自定义过虑器
        - id: open-api
          uri: lb://open-api
          order: -1
          predicates:
            - Path=/api/open/**
          filters:
            - StripPrefix=2
            - SwaggerHeaderFilter
#白名单(不鉴权)
setting:
  whiteUrls:
    - "/api/admin/auth/login"
    - "/api/admin/v2/api-docs"
    - "/api/pc/v2/api-docs"
    - "/api/open/v2/api-docs"

---

spring:
  profiles: dev
  redis:
    host: 10.10.10.35
    port: 6379
    password: root

eureka:
  instance:
    prefer-ip-address: true
    #Eureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒,超过则剔除
    lease-expiration-duration-in-seconds: 30
    #Eureka客户端向服务端发送心跳的时间间隔,单位为秒
    lease-renewal-interval-in-seconds: 15
  client:
    serviceUrl:
      defaultZone: http://10.10.10.35:8761/eureka/

---

spring:
  profiles: uat
  redis:
    host: 172.17.0.12
    port: 6379
    password: root

eureka:
  instance:
    prefer-ip-address: true
  client:
    serviceUrl:
      defaultZone: http://172.17.0.12:8761/eureka/

全局过滤

@Slf4j
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    private static final String START_TIME = "startTime";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getAttributes().put(START_TIME, System.currentTimeMillis());
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        String ip = IpUtil.getIp(serverHttpRequest);
        String method = serverHttpRequest.getMethod().name();
        String requestURI = serverHttpRequest.getURI().getPath();
        String token = serverHttpRequest.getHeaders().getFirst("token");


        return chain.filter(exchange).then( Mono.fromRunnable(() -> {
            Long startTime = exchange.getAttribute(START_TIME);
            if (startTime != null) {
                Long executeTime = (System.currentTimeMillis() - startTime);
                log.info(String.format("%s >>> %s >>> %s >>> %s >>> %s ms", requestURI, method, ip, token, executeTime));
            }
        }));

    }

    @Override
    public int getOrder() {
        return -2;
    }

}

登录过滤

@Slf4j
@Component
public class AdminAuthFilter extends AbstractGatewayFilterFactory implements Ordered {

    @Autowired
    private GatewaySetting gatewaySetting;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String requestURI = "/api/admin"+request.getURI().getPath();
            if(gatewaySetting.getWhiteUrls().contains(requestURI)){
                return chain.filter(exchange);
            }
            boolean isCookieToken = false;
            String token = request.getHeaders().getFirst("token");
            if(StringUtils.isEmpty(token)){
                MultiValueMap<String, HttpCookie> cookieValueMap = request.getCookies();
                log.debug("cookieValueMap===============>"+ JSON.toJSONString(cookieValueMap));
                if(cookieValueMap.containsKey(GlobalConstant.ADMIN_LOGIN_TOKEN_COOKIE_NAME)){
                    HttpCookie cookie = cookieValueMap.getFirst(GlobalConstant.ADMIN_LOGIN_TOKEN_COOKIE_NAME);
                    token = cookie.getValue();
                    isCookieToken = true;
                }
            }
            if(StringUtils.isEmpty(token)){
                return FilterUtil.failResponse(exchange, Code.UNAUTHORIZED,"非法访问");
            }
            if(!redisUtil.hasKey(RedisKeyConstant.adminApiAuthLoginToken+token)){
                return FilterUtil.failResponse(exchange,Code.EXPIRE_LOGIN,"登录过期");
            }
            if(isCookieToken){
                ServerHttpRequest host = exchange.getRequest().mutate().header("token", token).build();
                ServerWebExchange build = exchange.mutate().request(host).build();
                return chain.filter(build);
            }
            return chain.filter(exchange);
        };
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

白名单配置

@Getter
@Setter
@ConfigurationProperties("setting")
@Component
public class GatewaySetting {

    private List<String> whiteUrls;

}

工具类

public class FilterUtil {

    public static Mono<Void> failResponse(ServerWebExchange exchange, Code code, String msg){
        ServerHttpResponse response = exchange.getResponse();
        Result resp = Result.of(code,msg);
        byte[] bits = JSON.toJSONString(resp).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        //指定编码,否则在浏览器中会中文乱码
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }

}

public class IpUtil {

    private static final Log log = LogFactory.getLog(IpUtil.class);

    public static String getIp(ServerHttpRequest request) {
        String ip=null;

        List<String> headers =  request.getHeaders().get("X-Real-IP");
        if(headers!=null&&headers.size()>=1)
            ip = headers.get(0);

        if (!StringUtils.isEmpty(ip) && !"unknown".equalsIgnoreCase(ip)) {
            log.debug(">>>>>>>>>>>>>>>>>>>>>X-Real-IP获取到ip:"+ip);
            return ip;
        }
        headers =  request.getHeaders().get("X-Forwarded-For");
        if (!StringUtils.isEmpty(headers) && headers.size()>=1) {
            // 多次反向代理后会有多个IP值,第一个为真实IP。
            ip = headers.get(0);
            int index = ip.indexOf(',');
            if (index != -1) {
                log.debug(">>>>>>>>>>>>>>>>>>>>>X-Forwarded-For获取到ip:"+ip);
                return ip.substring(0, index);
            } else {
                return ip;
            }
        } else {
            log.debug(">>>>>>>>>>>>>>>>>>>>>RemoteAddress获取到ip:"+ip);
            return request.getRemoteAddress().getAddress().getHostAddress();
        }
    }
}

集成swagger

@RestController
@RequestMapping("/swagger-resources")
public class SwaggerHandler {
    @Autowired(required = false)
    private SecurityConfiguration securityConfiguration;
    @Autowired(required = false)
    private UiConfiguration uiConfiguration;
    private final SwaggerResourcesProvider swaggerResources;

    @Autowired
    public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
        this.swaggerResources = swaggerResources;
    }


    @GetMapping("/configuration/security")
    public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    @GetMapping("/configuration/ui")
    public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    @GetMapping("")
    public Mono<ResponseEntity> swaggerResources() {
        return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
    }
}
@Component
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {

    private static final String HEADER_NAME = "X-Forwarded-Prefix";

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getURI().getPath();
            if (!StringUtils.endsWithIgnoreCase(path, SwaggerProvider.API_URI)) {
                return chain.filter(exchange);
            }
            //String basePath = path.substring(0, path.lastIndexOf(SwaggerProvider.API_URI));
            String referName = "后台API";
            String referUrl = exchange.getRequest().getHeaders().get("Referer").get(0);
            if (referUrl.indexOf("=") > -1) {
                referName = referUrl.split("=")[1];
            }
            String basePath = "";
            try {
                basePath = SwaggerProvider.moduleMap.get(URLDecoder.decode(referName, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
            ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
            return chain.filter(newExchange);
        };
    }
}
@Component
@Primary
//@Profile({"dev","test"})
public class SwaggerProvider implements SwaggerResourcesProvider {

    public static final String API_URI = "/v2/api-docs";

    public static Map<String, String> moduleMap = new HashMap<>();

    static {
        moduleMap.put("后台API", "/api/admin");
        moduleMap.put("PC端API", "/api/pc");
        moduleMap.put("开放平台", "/api/open");
    }

    @Override
    public List<SwaggerResource> get() {
        List resources = new ArrayList<>();
        moduleMap.forEach((k, v) -> {
            resources.add(swaggerResource(k, v));
        });
        return resources;
    }

    private SwaggerResource swaggerResource(String name, String location) {
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(location + API_URI);
        swaggerResource.setSwaggerVersion("2.0");
        return swaggerResource;
    }
}

免责声明:文章转载自《Springcloud之gateway配置及swagger集成》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇[Linux之旅一] .NET Core 2.2部署到Docker中FFMPEG 内部 YUV444p16LE-&amp;gt; P016LE下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

java面试第二弹

1.==和equals的区别? == 指向的是一个地址 Equelas 指向的是地址里具体的值 2.静态变量和实例变量的区别? 静态变量依赖于类而存在,如果类改变了,静态变量也会跟着改变 实例变量不管其他类变不变,它自己的改变不会跟着其他东西改变 3.integer与int的区别? Int是java提供的八大原始数据类型之一。Java为每个原始类型都提供了...

RabbitMQ 消息应答机制

一、概述 消费者处理一个任务是需要一段时间的,如果有一个消费者正在处理一个比较耗时的任务并且只处理了一部分,突然这个时候消费者宕机了,那么会出现什么情况呢? 要回答这个问题,我们先了解一下 RabbitMQ 的消息应答机制 为了保证消息从队列可靠地达到消费者并且被消费者消费处理,RabbitMQ 提供了消息应答机制,RabbitMQ 有两种应答机制,自动应...

Dapper解析嵌套的多层实体类

在作项目的时候,我会将一些不涉及查询的字段,形成JSON统一存放在一个字段中,向下面这样的来建实体类, public class WechatModel { public string wechatid { get; set; } public WxMpModel wxmpinfo { get; set; } } p...

dynamic-insert和dynamic-update属性

dynamic-insert 作用:设置对象中没有值的字段 insert并不会对其进行插入. 实体类映射配置如下 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.o...

关于JavaScriptInterface的一系列问题

先明确主题,主要做了什么。 接手时app主体是混合开发,以elipse+android SDK为开发环境,但我是个渣渣,我只会用AS,就转成了AS,这部分等会新开文章写。 主要的view只有一个activity_main,里边写了个webview,在mainactivity里去掉了actionbar,一个导入库都没有有四个jar包,然后所有的HTML代码放...

SpringBoot入门 (四) 数据库访问之JdbcTemplate

  本文记录在SpringBoot中使用JdbcTemplate访问数据库。 一 JDBC回顾   最早是在上学时接触的使用JDBC访问数据库,主要有以下几个步骤: 1 加载驱动 Class.forName(Driver) 2 获取数据库连接 conn = DriverManager.getConnection(url, user,password) 3...