一,疑问
1.之前遇到跨域问题是在NG中解决的,添加跨域请求头和域名配置。那么与网关处理跨域问题关系是什么,NG处理了,为什么还需要在网关中处理
二,前置知识
zuul概念与原理
zuul 的概念和原理 - 知乎
Zuul工作原理_zuul原理_吴声子夜歌的博客-CSDN博客
梳理几个基本概念
zuul的工作原理
1.过滤器机制
zuul的核心是一系列的filters, 其作用可以类比Servlet框架的Filter,或者AOP。
zuul把Request route到 用户处理逻辑 的过程中,这些filter参与一些过滤处理,比如Authentication,Load Shedding等
2.过滤器机制过滤器类型
Zuul大部分功能都是通过过滤器来实现的。Zuul中定义了四种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。
(1) PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
(2) ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
(3) POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
(4) ERROR:在其他阶段发生错误时执行该过滤器
总结:通过一个个过滤器实现不同的业务逻辑,且不同过滤器类型按照顺序执行对应的业务
三,此文网关做了那几件事
1.PRE,前置需要处理的逻辑,跨域/XSS处理
2.POST,微服务处理之后记录处理时间等日志信息
具体处理逻辑
1.跨域问题重现,前端报错,如下包含了请求源Origin信息
2. ZuulFilter1跨域/xss攻击代码
Component
@RefreshScope
public class TimeCostPreFilter extends ZuulFilter {
public static final String START_TIME_KEY = "start_time";
public static final String ZUUL_REQUEST_URL_KEY = "zuul_request_start_time";
private static final Logger logger = LoggerFactory.getLogger(TimeCostPreFilter.class);
@Value("${com.gateway.xss.check:true}")
private Boolean xssCheck;
@Value("${com.gateway.xss.url.filter:uploadFile,uploadImage}")
private String urlFilter;
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
/**
* 判断是否要拦截的逻辑
*/
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
long startTime = System.currentTimeMillis();
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String nextElement = headerNames.nextElement();
if (nextElement.equalsIgnoreCase("origin") && request.getHeader(nextElement) != null) {
String str = request.getHeader(nextElement).toLowerCase();
String pattern = "^(http|https)://(.*\\.skcloud\\.cn|.*\\.skcloud\\.com|.*\\.sk\\.com|localhost:?[0-9]*|api.rrx.cn|c2bt4.skcloud.com)$";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(str);
if (!m.matches()) {
return null;
}
}
}
}
StringBuffer requestUrl = request.getRequestURL();
String url = requestUrl.toString();
if (xssCheck) {
if (!StringUtils.isEmpty(urlFilter)) {
String[] urlFilterList = urlFilter.split(",");
for (String s : urlFilterList) {
if (!StringUtils.isEmpty(urlFilter) && url.contains(s)) {
currentContext.set(ZUUL_REQUEST_URL_KEY, url);
currentContext.set(START_TIME_KEY, startTime);
return null;
}
}
}
try {
Map<String, String[]> map = request.getParameterMap();
if (!(Objects.isNull(map) || map.isEmpty())) {
for (Map.Entry<String, String[]> entry : map.entrySet()) {
String[] value = entry.getValue();
if (extracted(currentContext, value[0])) {
return null;
}
}
}
InputStream in = request.getInputStream();
String body = StreamUtils.copyToString(in, StandardCharsets.UTF_8);
if (!StringUtils.isEmpty(body)) {
if (extracted(currentContext, body)) {
return null;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
currentContext.set(ZUUL_REQUEST_URL_KEY, url);
currentContext.set(START_TIME_KEY, startTime);
return null;
}
private static boolean extracted(RequestContext currentContext, String body) {
boolean b = checkXss(body);
if (b) {
currentContext.setSendZuulResponse(false);
currentContext.setResponseBody("非法请求");
currentContext.setResponseStatusCode(HttpStatus.BAD_REQUEST.value());
return true;
}
return false;
}
public static boolean checkXss(String str) {
if (StringUtils.isEmpty(str)) {
return false;
}
boolean contains = str.contains("<");
boolean contains1 = str.contains(">");
boolean onclick = str.contains("onclick");
return contains || contains1 || onclick;
}
3. ZuulFilter2,日志记录
@Component
@RefreshScope
public class TimeCostPostFilter extends ZuulFilter {
private static final String START_TIME_KE = "start_time";
public static final String ZUUL_REQUEST_URL_KEY = "zuul_request_start_time";
private static final Logger logger = LoggerFactory.getLogger(TimeCostPostFilter.class);
@Autowired
private GateWayRequestTimeClient gateWayRequestTimeClient;
@Override
public String filterType() {
return FilterConstants.POST_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext currentContext = RequestContext.getCurrentContext();
String requestUrl = (String) currentContext.get(ZUUL_REQUEST_URL_KEY);
long startTime = (long) currentContext.get(START_TIME_KE);
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
int responseStatusCode = currentContext.getResponseStatusCode();
gateWayRequestTimeClient.insertLog(startTime, endTime, resultTime, requestUrl, responseStatusCode);
return null;
}
}