關於引入gateway的好處我網上找了下:java
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加註解web
@EnableFeignClients @EnableDiscoveryClient
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(); } } }
@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; } }