分享程序网
首页
  • java
微服务
微前端
环境搭建
数据库
设计模式
算法
软件
解决问题
链接
首页
  • java
微服务
微前端
环境搭建
数据库
设计模式
算法
软件
解决问题
链接
  • 微服务

    • 介绍
  • 微服务搭建

    • 初步搭建
  • 服务发现

    • Eureka
    • nacos
  • 网关

    • zuul
    • 网关配置
    • 过滤器
    • 动态加载
  • 认证(Oauth)

    • 初始化项目
    • Oauth2配置
    • 对外接口
  • 通用服务

    • 通用功能父模块
    • redis
  • 任务调度

    • 任务调度服务
    • xxl-job示例
  • 业务服务

    • 业务设计

过滤器

一般来说,过滤器是对请求的处理,允许某些请求访问,禁止某些请求访问。在zuul中不仅可以过滤,还可以进行加工,再下发到下游服务。

在这里写几个网关常用过滤器的示例。

白名单

白名单,只允许某些请求访问。

package com.shareprog.zuul.config.filter;

import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @ClassName: IpWhiteListFilter
 * @Description: 白名单
 * @author cl
 * @date 2021年5月24日
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class IpWhiteListFilter extends ZuulFilter {

	private static final String SELECT_IP_WHITELIST_SQL = "select ip from zuul_whitelist";
	
	private static final String WHITELIST_PREFIX = "zuul:whitelist";
	
	private static final String UNKNOWN = "unknown";
	
	private final JdbcTemplate jdbcTemplate;
	
	private final RedisTemplate<String, String> redisTemplate;

	@Override
	public boolean shouldFilter() {
		return false;
	}

	@Override
	public Object run() throws ZuulException {
		RequestContext currentContext = RequestContext.getCurrentContext();
		HttpServletRequest httpServletRequest = currentContext.getRequest();
		String ipAddr = getIpAddress(httpServletRequest);
		log.info("请求地址IP为:{}", ipAddr);
		List<String> whitelist = redisTemplate.opsForList().range(WHITELIST_PREFIX, 0, -1);
		if (whitelist.isEmpty()) {
			whitelist = jdbcTemplate.queryForList(SELECT_IP_WHITELIST_SQL, String.class);
		}
		if (!whitelist.contains(ipAddr)) { 
			currentContext.setSendZuulResponse(false);
			currentContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
			currentContext.getResponse().setContentType(MediaType.APPLICATION_JSON_VALUE);
		}
		return null;
	}

	@Override
	public String filterType() {
		return FilterConstants.PRE_TYPE;
	}

	@Override
	public int filterOrder() {
		return FilterConstants.SEND_ERROR_FILTER_ORDER;
	}
	
	/**
	 * 获取真实IP
	 * @param request
	 * @return
	 */
	private static String getIpAddress(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if (judgeIp(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (judgeIp(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (judgeIp(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (judgeIp(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (judgeIp(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}

	private static boolean judgeIp(String ip) {
		return ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip);
	}
}

限流

限制某个时间段中只允许请求多少次,以下示例是每秒1000次。

package com.shareprog.zuul.config.filter;

import java.util.concurrent.TimeUnit;

import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
@RequiredArgsConstructor
public class LimitFilter extends ZuulFilter {
	
	private static volatile RateLimiter rateLimiter = RateLimiter.create(100.0);
	
	private final RedisTemplate<String, Object> redisTemplate;

	@Override
	public boolean shouldFilter() {
		return false;
	}

	@Override
	public Object run() throws ZuulException {
		RequestContext currentContext = RequestContext.getCurrentContext();
		Long currentSecond = System.currentTimeMillis() / 1000;
		String key = "dts-api-rate-limit-" + currentSecond;
		
		try {
			if (!redisTemplate.hasKey(key)) {
				redisTemplate.opsForValue().set(key, 0L, 100L, TimeUnit.SECONDS);
			}
			int rate = 10;
			if (redisTemplate.opsForValue().increment(key, 1L) > rate) {
				currentContext.setSendZuulResponse(false);
				currentContext.setResponseStatusCode(HttpStatus.SERVICE_UNAVAILABLE.value());
				return null;
			}
		} catch (Exception e) {
			log.error("集群限流异常", e);
			//单节点限流
			rateLimiter.acquire();
		}
		return null;
	}

	@Override
	public String filterType() {
		return FilterConstants.PRE_TYPE;
	}

	@Override
	public int filterOrder() {
		return FilterConstants.SEND_ERROR_FILTER_ORDER + 1;
	}

}

动态加载过滤器

添加Groovy依赖:

    <dependency>
        <groupId>org.codehaus.groovy</groupId>
        <artifactId>groovy-all</artifactId>
        <version>3.0.13</version>
    </dependency>

添加动态执行的代码

package com.shareprog.zuul.config.zuul;

import com.netflix.zuul.FilterFileManager;
import com.netflix.zuul.FilterLoader;
import com.netflix.zuul.groovy.GroovyCompiler;
import com.netflix.zuul.groovy.GroovyFileFilter;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.File;

/**
 * groovy 网关脚本执行
 *
 * @author cl
 * @date 2022/11/29
 */
@Component
@Order(value = 1)
public class GroovyLoadLineRunner implements CommandLineRunner {
    /**
     * Callback used to run the bean.
     *
     * @param args incoming main method arguments
     * @throws Exception on error
     */
    @Override
    public void run(String... args) throws Exception {
        FilterLoader.getInstance().setCompiler(new GroovyCompiler());
        //读取配置,获取脚本根目录
        String scriptRoot = System.getProperty("zuul.filter.root", "groovy/filters");
        //获取刷新间隔
        String refreshInterval = System.getProperty("zuul.filter.refreshInterval", "5");
        if (scriptRoot.length() > 0) {
            scriptRoot = scriptRoot + File.separator;
        }
        FilterFileManager.setFilenameFilter(new GroovyFileFilter());
        FilterFileManager.init(Integer.parseInt(refreshInterval), scriptRoot + "pre",
                scriptRoot + "route", scriptRoot + "post");
    }
}

groovy脚本在src同级目录的groovy/filters/pre下

import com.netflix.zuul.ZuulFilter
import com.netflix.zuul.context.RequestContext
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants
import javax.servlet.http.HttpServletRequest

class GroovyFilter extends ZuulFilter {
    private static final Logger LOGGER = LoggerFactory.getLogger(GroovyFilter.class)

    @Override
    String filterType() {
        return FilterConstants.PRE_TYPE
    }

    //过滤器优先级
    @Override
    int filterOrder() {
        return 5
    }

    @Override
    boolean shouldFilter() {
        return true
    }

    @Override
    Object run() {
        HttpServletRequest request = (HttpServletRequest) RequestContext.getCurrentContext().getRequest()
        Enumeration<String> headerNames = request.getHeaderNames()
        while (headerNames.hasMoreElements()) {
            String name = (String) headerNames.nextElement()
            String value = request.getHeader(name)
            LOGGER.info("header: " + name + ":" + value)
        }
        LOGGER.info("This is Groovy Filter")
        return null
    }

}

Last Updated:
Contributors: chengli, clcheng
Prev
网关配置
Next
动态加载