2020-12-06

Springcloud之gateway配置及swagger集成

前言

关于引入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: truespring: 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: rooteureka: instance: prefer-ip-address: true #Eureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒,超过则剔除 lease-expiration-duration-in-seconds: 30 #Eureka客户端向服务端发送心跳的时间间隔,单位为秒 lease-renewal-interval-in-seconds: 15 client: serviceUrl:  defaultZone: profiles: uat redis: host: 172.17.0.12 port: 6379 password: rooteureka: instance: prefer-ip-address: true client: serviceUrl:  defaultZone: id="全局过滤">全局过滤
@Slf4j@Componentpublic 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@Componentpublic 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")@Componentpublic 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))); }}
@Componentpublic 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; }}








原文转载:http://www.shaoqun.com/a/494971.html

墩煌网:https://www.ikjzd.com/w/189

dojo:https://www.ikjzd.com/w/2052

r标:https://www.ikjzd.com/w/1070


前言关于引入gateway的好处我网上找了下:性能:API高可用,负载均衡,容错机制。安全:权限身份认证、脱敏,流量清洗,后端签名(保证全链路可信调用),黑名单(非法调用的限制)。日志:日志记录(spainid,traceid)一旦涉及分布式,全链路跟踪必不可少。缓存:数据缓存。监控:记录请求响应数据,api耗时分析,性能监控。限流:流量控制,错峰流控,可以定义多种限流规则。灰度:线上灰度部署,可
聚贸:聚贸
usps:usps
珠海竹仙洞自驾游该怎么走啊?:珠海竹仙洞自驾游该怎么走啊?
茂名浪漫海岸好玩吗?:茂名浪漫海岸好玩吗?
亚马逊这些敏感词要小心了!:亚马逊这些敏感词要小心了!

No comments:

Post a Comment