一、前戏-- 功能需求
通过上一篇对soul插件链的整体概览学习,本篇开始“生搬硬套”,尽量符合soul的编码方式实现一个自定义单一职责插件。
需求:实现一个参数验签的插件,验签失败中断调用链调用直接返回错误信息,否则执行剩下调用链。
二、自定义插件类
- 在soul-plugin项目下,新建一个子模块soul-plugin-customsign。创建SoulPlugin接口的实现类CustomSignPlugin。
- 实现SoulPlugin接口定义方法,各方法职责如下:
execute()
方法为核心的执行方法,用户可以在里面自由的实现自己想要的功能。
getOrder()
指定插件的排序。
named()
指定插件的名称。
skip()
在特定的条件下,该插件是否被跳过。
此处附上一个小demo:
@Slf4j
public class CustomSignPlugin implements SoulPlugin {
/**
* 加密私钥
*/
private String privateKey = "CDRjzk2sb99v6nUkXx8+6g";
@Override
public String named() {
return PluginEnum.CUSTOMSIGN.getName();
}
@Override
public Boolean skip(final ServerWebExchange exchange) {
final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
return !Objects.equals(Objects.requireNonNull(soulContext).getRpcType(), RpcTypeEnum.HTTP.getName());
}
@Override
public Mono<Void> execute(ServerWebExchange exchange, SoulPluginChain chain) {
final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
String queryParam = exchange.getRequest().getURI().getQuery();
if (StringUtils.isNotBlank(queryParam)) {
if(!checkSignature(exchange.getRequest())) {
log.error("接口请求验签失败");
Object error = SoulResultWrap.error(SoulResultEnum.CHECK_CUSTOM_SIGN_EXCEPTION.getCode(), SoulResultEnum.CHECK_CUSTOM_SIGN_EXCEPTION.getMsg(), null);
return WebFluxResultUtils.result(exchange, error);
}
}
return chain.execute(exchange);
}
private boolean checkSignature(ServerHttpRequest request) {
Map<String, String> map = request.getQueryParams().toSingleValueMap();
String signReq = map.containsKey("sign")? map.get("sign").toString() : null;;
boolean checkSignture = false;
//验证签名
if (map.size() > 0 && StringUtils.isNotBlank(signReq)) {
//签名匹配,则签名认证通过
map.remove("sign");
if (signReq.equals(Signature.getSign(map, privateKey))
|| signReq.equalsIgnoreCase(Signature.getSign(map, privateKey))) {
checkSignture = true;
}
} else {
// 不传签名,非法强求
checkSignture = false;
}
return checkSignture;
}
@Override
public int getOrder() {
return PluginEnum.CUSTOMSIGN.getCode();
}
- soul-plugin-customsign目录结构如下:
- 调整pom, install 本地仓库,待引用
三、设置插件执行顺序
在PluginEnum类中创建,自定义插件的执行顺序,名字等信息。
ps:1.枚举中code定义相对松散,这个间隔目前怀疑是为了便于“渗入”新插件
image.png2.枚举类中的name一定要定义好且具有唯一性,后文配置中还会用到
四、设置异常
SoulResultEnum类中定义统一错误异常码,此处建议使用英文,此处用中文作以区分。
image.png五、封装spring-starter
- 在soul-spring-boot-starter-plugin项目下,创建子模块soul-spring-boot-starter-plugin-customsign
- 引入自定义插件依赖
<dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-plugin-customsign</artifactId>
<version>${project.version}</version>
</dependency>
- 此类只是对做一个简单的spring-starter封装,封装一个CustomSignPluginConfiguration
@Configuration
public class CustomSignPluginConfiguration {
/**
* init SoulPlugin.
*
* @return {@linkplain SoulPlugin}
*/
@Bean
public SoulPlugin customSignPlugin() {
return new CustomSignPlugin();
}
}
- 当然不要忘记加上加载bean的配偶spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.dromara.soul.spring.boot.plugin.divide.DividePluginConfiguration
- 整体目录结构如下
六、soul-bootstrap的配置
此处配置就“过于”简单,只需要引入maven依赖即可,体现出作者对代码的高内聚、低耦合了,nice!
<pre><dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-spring-boot-starter-plugin-customsign</artifactId>
<version>${project.version}</version>
</dependency></pre>
七、配置插件信息
- 整体项目mvn一下
mvn clean package install -Dmaven.test.skip=true -Dmaven.javadoc.skip=true -Drat.skip=true -Dcheckstyle.skip=true
- 插件管理中配置插件信息,此处插件名正式前文PluginEnum中定义name,一定保持一致否则插件链初始化将失败,惨痛浪费15分钟调试经验。
八、测试
- 分别启动soul-bootstrap、soul-plugin-customsign测试一下
一发入魂,搞定! 收拾收拾可以再睡一会了。
九、小结
- 知易行难:感觉自己捋清了调用链,实现一个自定义插件应该还算easy。实际操作起来,其实~~~~我还是很菜啊!
- 回顾整个实现流程,确实很精巧。热插拔的插件链,确实很精巧,原有代码基本不需要改动,确实高内聚低耦合。确实很精巧!!!
- 日拱一卒,每天进步一点点
网友评论