springcloudgateway写2个filter,一个GlobalFilter实现公共token的校验,一个实现具体应用的拦截器逻辑。
package com.example.gate.filter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.synchronoss.cloud.nio.multipart.util.IOUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public class TokenFilter implements GlobalFilter, Ordered {
Logger logger = LoggerFactory.getLogger(TokenFilter.class);
@Override
public int getOrder() {
// TODO Auto-generated method stub
return 120;
}
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
logger.info("TokenFilter开始............");
getAllParamtersRequest(exchange.getRequest());
getAllHeadersRequest(exchange.getRequest());
//拦截的逻辑。根据具体业务逻辑做拦截。
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (token == null || token.isEmpty()) {
logger.info("token is empty...");
// exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
// return exchange.getResponse().setComplete();
//设置status和body
return Mono.defer(() -> {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);//设置status
final ServerHttpResponse response = exchange.getResponse();
byte[] bytes = "{\"code\":\"99999\",\"message\":\"非法访问,没有检测到token~~~~~~\"}".getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
response.getHeaders().set("aaa", "bbb");//设置header
logger.info("TokenFilter拦截非法请求,没有检测到token............");
return response.writeWith(Flux.just(buffer));//设置body
});
}
//没有被if条件拦截,就放行
return chain.filter(exchange);
}
private Map getAllParamtersRequest(ServerHttpRequest request) {
logger.info("getAllParamtersRequest开始............");
Map map = new HashMap();
MultiValueMap paramNames = request.getQueryParams();
Iterator it= paramNames.keySet().iterator();
while (it.hasNext()) {
String paramName = (String) it.next();
List paramValues = paramNames.get(paramName);
if (paramValues.size() >= 1) {
String paramValue = paramValues.get(0);
logger.info("request参数:"+paramName+",值:"+paramValue);
map.put(paramName, paramValue);
}
}
return map;
}
private Map getAllHeadersRequest(ServerHttpRequest request) {
logger.info("getAllHeadersRequest开始............");
Map map = new HashMap();
HttpHeaders hearders = request.getHeaders();
Iterator it= hearders.keySet().iterator();
while (it.hasNext()) {
String keyName = (String) it.next();
List headValues = hearders.get(keyName);
if (headValues.size() >= 1) {
String kvalue = headValues.get(0);
logger.info("request header的key:"+keyName+",值:"+kvalue);
map.put(keyName, kvalue);
}
}
return map;
}
// /**
// *
// * @param request
// * @return
// */
// private Map getAllParamtersRequest(HttpServletRequest request) {
// logger.info("getAllParamtersRequest............");
// Map map = new HashMap();
// Enumeration paramNames = request.getParameterNames();
// while (paramNames.hasMoreElements()) {
// String paramName = (String) paramNames.nextElement();
// String[] paramValues = request.getParameterValues(paramName);
// if (paramValues.length >= 1) {
// String paramValue = paramValues[0];
// if (paramValue.length() != 0) {
// logger.info("request参数:"+paramName+",值:"+paramValue);
// map.put(paramName, paramValue);
// }
//
// }
// }
// return map;
// }
}
package com.example.gate.filter;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
*
* Ordered 负责filter的顺序,数字越小越优先,越靠前。
*
* GatewayFilter:
* 需要通过spring.cloud.routes.filters 配置在具体路由下,
* 只作用在当前路由上或通过spring.cloud.default-filters配置在全局,作用在所有路由上。
* 需要用代码的形式,配置一个RouteLocator,里面写路由的配置信息。
*
* GlobalFilter:
* 全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,
* 它为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。
* 代码配置需要声明一个GlobalFilter对象。
*
*
* 对一个应用来说,GatewayFilter和GlobalFilter是等价的,order也会按照顺序进行拦截。所以两个order不要写一样!
*
* @author lsy
*
*/
public class UrlFilter implements GatewayFilter, Ordered {
Logger logger = LoggerFactory.getLogger(UrlFilter.class);
@Override
public int getOrder() {
return 0;
}
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
logger.info("UrlFilter开始............");
URI uri = exchange.getRequest().getURI();
logger.info("UrlFilter开始............URI==="+uri.toString());
HttpMethod method = exchange.getRequest().getMethod();
logger.info("UrlFilter开始............HttpMethod==="+method.toString());
String methodValue= exchange.getRequest().getMethodValue();
logger.info("UrlFilter开始............getMethodValue==="+methodValue);
RequestPath requestPath =exchange.getRequest().getPath();
logger.info("UrlFilter开始............RequestPath==="+requestPath.toString());
InetSocketAddress inetSocketAddress =exchange.getRequest().getRemoteAddress();
logger.info("UrlFilter开始............InetSocketAddress==="+inetSocketAddress.toString());
//拦截的逻辑。根据具体业务逻辑做拦截。
String perm = exchange.getRequest().getQueryParams().getFirst("perm");
if (perm == null || perm.isEmpty()) {
logger.info("perm is empty...");
//设置HttpStatus,返回体是空的
// exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
// return exchange.getResponse().setComplete();
//下面可以设置返回体的具体内容
//设置status和body
return Mono.defer(() -> {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);//设置status
final ServerHttpResponse response = exchange.getResponse();
byte[] bytes = "{\"code\":\"99000\",\"message\":\"非法访问,没有检测到权限码perm~~~~~~\"}".getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
response.getHeaders().set("permValid", "false");//设置header
logger.info("UrlFilter拦截非法请求,没有检测到权限码perm............");
return response.writeWith(Flux.just(buffer));//设置body
});
}
//没有被if条件拦截,就放行
return chain.filter(exchange);
}
}
package com.example.gate.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.example.gate.filter.TokenFilter;
import com.example.gate.filter.UrlFilter;
@Configuration
public class FilterConfig {
Logger logger = LoggerFactory.getLogger(FilterConfig.class);
@Bean
public GlobalFilter tokenFilter() {
return new TokenFilter();
}
@Bean
public RouteLocator urlFilterRouteLocator(RouteLocatorBuilder builder) {
logger.info("FilterConfig---urlFilterRouteLocator---");
return builder.routes()
.route(r -> r.path("/one/**")
.filters(f -> f.stripPrefix (1).filter(new UrlFilter())
.addResponseHeader("urlFilterFlag", "pass"))
.uri("lb://one")
.order(0)
.id("one")
)
.build();
}
}
server:
port: 8888
spring:
application:
name: gate
cloud:
consul:
host: 127.0.0.1 #注册中心的ip或host。也是集群地址,配置一个即可。
port: 8500
discovery:
#enabled: true #使用服务发现客户端定义路由信息。即不配置routes节点,采用默认配置;配置了,以配置为准。默认true。
register: true #是否将服务注册到Consul集群中心
deregister: true #默认true,服务停止时注销服务,即从服务列表中删除。设置成false的话,???
#service-name: ${spring.application.name} #注册在consul上面的名字,在consul的调用中,是通过此名字调用的。默认服务名,不要改
instance-id: ${spring.application.name}-${spring.cloud.client.ip-address}:${server.port} #只供显示用,在ID列显示
health-check-interval: 5s #配置 Consul 健康检查频率,也就是心跳频率。
health-check-critical-timeout: 1s #健康检查失败多长时间后,取消注册。在node上显示红色叉。
#health-check-path: /tmp #健康检查路径
#prefer-ip-address: true #表示注册时使用IP而不是hostname
gateway:
discovery:
locator:
enabled: true #表明gateway开启服务注册和发现的功能,并且自动根据服务发现为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务
lower-case-service-id: true # 服务名小写
# routes:
# - id: one
# # lb代表从注册中心获取服务,且已负载均衡方式转发
# uri: lb://one #目标服务地址
# predicates: #路由条件
# - Path=/one/**
# #过滤规则。 StripPrefix=1标识去掉url的第一个前缀,用后面的内容拼接到uri后面
# filters:
# - StripPrefix=1
# - id: two
# # lb代表从注册中心获取服务,且已负载均衡方式转发
# uri: lb://two #目标服务地址
# predicates: #路由条件
# - Path=/two/**
# #过滤规则。 StripPrefix=1标识去掉url的第一个前缀,用后面的内容拼接到uri后面
# filters:
# - StripPrefix=1
#eureka:
# instance:
# prefer-ip-address: true
# instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
# lease-expiration-duration-in-seconds: 10
# lease-renewal-interval-in-seconds: 3
# client:
# registry-fetch-interval-seconds: 5
# serviceUrl:
# defaultZone: http://localhost:8761/eureka/
#启用监控.开启监控后,可直接通过actuator的rest接口查看服务的详细信息。例如查看网关的路由:http://localhost:8888/actuator/gateway/routes
management:
endpoints:
web:
exposure:
include:
- "*" # 开放所有端点health,info,metrics,通过actuator/+端点名就可以获取相应的信息。默认打开health和info
endpoint:
health:
show-details: always #未开启actuator/health时,我们获取到的信息是{"status":"UP"},status的值还有可能是 DOWN。开启后打印详细信息
我起2个微服务,一个叫one,一个叫two。都有一个测试的controller:
package com.example.one.controller;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
//@Component
@RestController
//@RequestMapping("/beat")
public class HelloController {
@Value("${spring.cloud.client.ip-address}")
private String ip;
@Value("${spring.application.name}")
private String servername;
@Value("${server.port}")
private String port;
// @Autowired
// private ComConfig conf;
@GetMapping(value="/hello")
public String hello() {
String[] colorArr=new String[] {"red","blue","green","pink","gray"};
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// LocalDateTime startTime = LocalDateTime.now();
String message="hello ! I am ["+servername+":"+ip+":"+port+"]"+"..."+LocalDateTime.now().format(df);
System.out.println(message);
return message;
}
}
根据以上代码,我定义了一个全局的过滤器TokenFilter,给one服务添加了UrlFilter(two没有)
经测试,全局拦截器对2个应用都起作用。UrlFilter只对one起作用。
值得注意的是,对于one,他有2个Filter。顺序是按照order来得。对于他来说已经不分GatewayFilter和GlobalFilter了。