记录网关zuul处理跨域/XSS问题

文章介绍了Zuul作为微服务网关如何处理跨域问题和防止XSS攻击。通过定义PRE过滤器检查请求源,允许特定域名,以及POST过滤器记录请求处理时间。同时,文章详细展示了ZuulFilter的实现逻辑,包括判断请求来源、拦截非法请求和记录日志。
摘要由CSDN通过智能技术生成

一,疑问

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;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值