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

依赖
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@EnableDiscoveryClientyml
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