一、前戏
waf插件,主要是用来对流量实现防火墙的核心功能:拦截非法请求、异常请求、拒绝策略
二、soul-admin配置
- 插件配置
在 soul-admin
–> 插件管理-> waf
设置为开启
注意模式的选择:
当 model
设置为 black
模式的时候,只有匹配的流量才会执行拒绝策略,不匹配的,直接会跳过。
当 model
设置为 mixed
模式的时候,所有的流量都会通过 waf插件,针对不同的匹配流量,用户可以设置是拒绝,还是通过。
- 选择器配置
采用通配符匹配,匹配/http开头的请求
image- 规则配置
为了后面测试,配置两天规则,通过状态开关进行切换
规则1: 匹配 http开头的所有 如:http://127.0.0.1:9195/http/order/findById?id=2
image测试结果如下:
image规则2:匹配http开头,有userId参数的url,如:http://127.0.0.1:9195/http/test/findByUserId?userId=1
image测试结果如下:
image.png三、soul-bootstrap配置
- pom引入依赖,重启网关
<dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-spring-boot-starter-plugin-waf</artifactId>
<version>${last.version}</version>
</dependency>
四、WafPlugin源码分析
在父类AbstractSoulPlugin中判断插件是否可用、匹配选择器、匹配规则
public abstract class AbstractSoulPlugin implements SoulPlugin {
public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
String pluginName = named();
// 获取插件配置
final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName);
// 是否开启插件,没有开启则传递下一下插件去处理
if (pluginData != null && pluginData.getEnabled()) {
// 获取选择器
final Collection<SelectorData> selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName);
if (CollectionUtils.isEmpty(selectors)) {
return handleSelectorIsNull(pluginName, exchange, chain);
}
// 匹配选择器
final SelectorData selectorData = matchSelector(exchange, selectors);
if (Objects.isNull(selectorData)) {
return handleSelectorIsNull(pluginName, exchange, chain);
}
selectorLog(selectorData, pluginName);
// 获取规则列表
final List<RuleData> rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId());
if (CollectionUtils.isEmpty(rules)) {
return handleRuleIsNull(pluginName, exchange, chain);
}
RuleData rule;
// 如果是full,则获取最近的一个规则
if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) {
//get last
rule = rules.get(rules.size() - 1);
} else {
// 根据规则匹配
rule = matchRule(exchange, rules);
}
if (Objects.isNull(rule)) {
return handleRuleIsNull(pluginName, exchange, chain);
}
ruleLog(rule, pluginName);
// 交由子类WafPlugin去处理
return doExecute(exchange, chain, selectorData, rule);
}
// 交由下一个插件去处理
return chain.execute(exchange);
}
对匹配规则的请求进行,进行拒绝策略的处理
public class WafPlugin extends AbstractSoulPlugin {
protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
// 获取Waf配置
WafConfig wafConfig = Singleton.INST.get(WafConfig.class);
// 如果没有匹配的选择器和规则
if (Objects.isNull(selector) && Objects.isNull(rule)) {
// 如果waf配置是black模式,则跳过。交由下一个插件处理
if (WafModelEnum.BLACK.getName().equals(wafConfig.getModel())) {
return chain.execute(exchange);
}
// 设置 403 status code
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
// 设置 error 信息
Object error = SoulResultWrap.error(HttpStatus.FORBIDDEN.value(), Constants.REJECT_MSG, null);
// 返回response
return WebFluxResultUtils.result(exchange, error);
}
// 获取规则配置信息
String handle = rule.getHandle();
WafHandle wafHandle = GsonUtils.getInstance().fromJson(handle, WafHandle.class);
// 如规则为Null或者permission为空,交由下一个插件处理
if (Objects.isNull(wafHandle) || StringUtils.isBlank(wafHandle.getPermission())) {
log.error("waf handler can not configuration:{}", handle);
return chain.execute(exchange);
}
// 如果permission是reject,则返回 403 status code
if (WafEnum.REJECT.getName().equals(wafHandle.getPermission())) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
Object error = SoulResultWrap.error(Integer.parseInt(wafHandle.getStatusCode()), Constants.REJECT_MSG, null);
// 返回response
return WebFluxResultUtils.result(exchange, error);
}
// 交由下一个插件处理
return chain.execute(exchange);
}
}
五、小结
- 在AbstractSoulPlugin中匹配选择器和规则,在WafPlugin中处理拒绝策略
- 日拱一卒
网友评论