嘉兴市做外贸网站的,有什么做海报的网站吗,wordpress 附件预览,株洲网【SpringBootStarter】
目的
了解SpringBoot Starter相关概念以及开发流程实现自定义SpringBoot Starter(全局加解密)了解测试流程优化
最终引用的效果#xff1a;
dependencygroupIdcom.xbhog/groupIdartifactIdglobalValidation-spring…【SpringBootStarter】
目的
了解SpringBoot Starter相关概念以及开发流程实现自定义SpringBoot Starter(全局加解密)了解测试流程优化
最终引用的效果
dependencygroupIdcom.xbhog/groupIdartifactIdglobalValidation-spring-boot-starter/artifactIdversion1.0.0/version
/dependency【开源地址】https://gitee.com/xbhog/encry-adecry-spring-boot-starter欢迎star
了解SpringBoot Starter相关概念以及开发流程
SpringBoot Starter
SpringBoot Starter作用将一组相关的依赖打包简化项目的配置和初始化过程通过特定的Starter开发者可以快速的实现特定功能模块的开发和扩展。
自定义Starter能够促进团队内部资源的复用保持项目间的一致性提升协作效率并且有助于构建稳定、高效的大型系统。
开发流程
注入SpringBoot的方式
在刚开始开发Starter的时候首先考虑的是怎么能注入到SpringBoot中
这部分涉及到部分SpringBoot的自动装配原理不太清楚的朋友可以补习下
注入SpringBoot需要配置文件在项目中的resources资源目录中创建该目录和文件。
demo-spring-boot-starter
└── src└── main└── java└── com.xbhog├── DemoBean.java└── DemoBeanConfig.java└── resources└── META-INF└── spring.factories在spring.factories中我们指定一下自动装配的配置类格式如下
org.springframework.boot.autoconfigure.EnableAutoConfigurationcom.xbhog.DemoBeanConfig/*** author xbhog* describe:*/
Slf4j
Configuration
public class DemoBeanConfig {Beanpublic DemoBean getDemo() {log.info(已经触发了配置类正在初始化DemoBean...);return new DemoBean();}
}Slf4j
public class DemoBean {public void getDemo(){log.info(方法调用成功);}
}这样就可以将设置的包扫描路径下的相关操作打包到SpringBoot 中。
SpringBoot主类启动器初始化的操作感兴趣的朋友可以研究下 完成后我们可以打包该项目然后在测试工程红进行Maven的引入、测试。
测试
新建Spring 测试工程引入依赖
dependencygroupIdcom.xbhog/groupIdartifactIddemo-spring-boot-starter/artifactIdversion1.0/version
/dependencyRestController
public class BasicController implements ApplicationContextAware {private ApplicationContext applicationContext;/**两种引入方式都可以Autowiredprivate DemoBean demoBean;*/GetMapping(/configTest)public void configTest() {DemoBean demoBean applicationContext.getBean(DemoBean.class);demoBean.getDemo();}Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext applicationContext;}
}
请求地址后可以观察控制台如下日志表示SpringBoot Starter可以使用了。 到此一个简单的Starter开发完成了后续可以围绕工程根据需求和业务对通用功能(接口操作日志、异常、加解密、白名单等)进行封装最后打到Maven仓库中进行使用。
自定义SpringBoot Starter(全局加解密)
来源
在之前金融系统开发中需要对接多个第三方的服务且数据安全性要求比较高在接口评审阶段需要双方在数据传输的时候进行接口加解密起初在第一个服务对接的时候将相关的加解密操作写到工具类中随着后续服务的增多代码的侵入越来越严重。
封装
选择通过Starter进行功能的封装好处引用方便开发迭代方便团队复用度高且对业务没有侵入。
开发
思路通过配置文件初始化让配置类注解ComponentScan扫描到的Bean等注入到SpringBoot中,通过自定义注解和RequestBodyAdvice/ResponseBodyAdvice组合拦截请求在BeforBodyRead/beforeBodyWrite中进行数据的前置处理解密后映射到接口接收的字段或对象。
接口上的操作有两种方式
注解AOP实现注解RequestBodyAdvice/ResponseBodyAdvice
这里我选择的第二种的RequestBodyAdvice/ResponseBodyAdvice抛砖引玉一下。
**【注】**第二种存在的局限性是只能针对POST请求中的Body数据处理无法针对GET请求进行处理。
项目结构
encryAdecry-spring-boot-starter
└── src└── main└── java└── com.xbhog├── advice│ ├──ResponseBodyEncryptAdvice.java│ └──RequestBodyDecryptAdvice.java├── annotation│ └──SecuritySupport├── handler│ ├──impl│ │ └──SecurityHandlerImpl.java│ └──SecurityHandler└── holder│ ├──ContextHolder.java│ ├──EncryAdecryHolder.java│ └──SpringContextHolder.java└──GlobalConfig.java└── resources└── META-INF└── spring.factories项目处理流程图 核心代码
Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class? extends HttpMessageConverter? converterType) throws IOException {log.info(进入【RequestBodyDecryptAdvice】beforeBodyRead的操作方法{},parameter.getMethod());SecuritySupport securitySupport parameter.getMethodAnnotation(SecuritySupport.class);assert securitySupport ! null;ContextHolder.setCryptHolder(securitySupport.securityHandler());String original IOUtils.toString(inputMessage.getBody(), Charset.defaultCharset());//todolog.info(该流水已插入当前请求流水表);String handler securitySupport.securityHandler();String plainText original;if(StringUtils.isNotBlank(handler)){SecurityHandler securityHandler SpringContextHolder.getBean(handler, SecurityHandler.class);plainText securityHandler.decrypt(original);}return new MappingJacksonInputMessage(IOUtils.toInputStream(plainText, Charset.defaultCharset()), inputMessage.getHeaders());
}Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {log.info(进入【ResponseBodyEncryptAdvice】beforeBodyWrite的操作方法{},returnType.getMethod());String cryptHandler ContextHolder.getCryptHandler();SecurityHandler securityHandler SpringContextHolder.getBean(cryptHandler, SecurityHandler.class);assert body ! null;return securityHandler.encrypt(body.toString());
}该Starter中的全局加解密默认采用的国密非对称加密SM2,在开发过程中遇到了该问题InvalidCipherTextException: invalid cipher text
【原因】 私钥和公钥值不是成对存在的每次调用SmUtil.sm2()会生成不同的随机密钥对。
【解决】在该Starter中采用PostConstruct修饰方法在项目运行中只会初始化运行一次该方法保证了SmUtil.sm2()只会调用一次不会生成不同的随机秘钥对。
【ISSUES#1890】详细请看该地址https://hub.fgit.cf/dromara/hutool/issues/1890
/*** author xbhog* date 2024/02/01 13:23**/
Slf4j
Component
public class EncryAdecryHolder {public static SM2 sm2 null;PostConstructpublic void encryHolder(){KeyPair pair SecureUtil.generateKeyPair(SM2);byte[] privateKey pair.getPrivate().getEncoded();byte[] publicKey pair.getPublic().getEncoded();log.info(生成的公钥{},publicKey);log.info(生成的私钥{},privateKey);sm2 SmUtil.sm2(privateKey, publicKey);}
}除了默认的加密方式还可以通过SecurityHandler接口进行扩展扩展出来的impl可以在SecuritySupport(securityHandlerxxxxxx)中指定。
/*** author xbhog* describe: 全局加解密注解* date 2023/6/8*/
Target(ElementType.METHOD)
Retention(RetentionPolicy.RUNTIME)
public interface SecuritySupport {/*securityHandlerImpl*/String securityHandler() default securityHandlerImpl;String exceptionResponse() default ;}测试
复用之前的测试项目引用打包的mavne依赖
dependencygroupIdcom.xbhog/groupIdartifactIdencryAdecry-spring-boot-starter/artifactIdversion1.0.0/version
/dependency启动项目初始化公私钥。 测试接口代码如下
Slf4j
RestController
public class BasicController implements ApplicationContextAware {Resource(name demoSecurityHandlerImpl)private SecurityHandler encryAdecry;private ApplicationContext applicationContext;// http://127.0.0.1:8080/hello?namelisi//SecuritySupport(securityHandler demoSecurityHandlerImpl)SecuritySupportPostMapping(/hello)public String hello(RequestBody String name) {return Hello name;}GetMapping(/configTest)public String configTest(RequestParam(name) String name) {/*DemoBean demoBean applicationContext.getBean(DemoBean.class);demoBean.getDemo();*/return encryAdecry.encrypt(name);//return MD5.create().digestHex16(name);}
}优化
优化后的项目结构
encryAdecry-spring-boot-starter
└── src└── main└── java└── com.xbhog├── advice│ ├──ResponseBodyEncryptAdvice.java│ └──RequestBodyDecryptAdvice.java├── annotation│ └──SecuritySupport├── handler│ ├──impl│ │ └──EncryAdecryImpl.java│ └──SecurityHandler└── holder│ ├──ContextHolder.java│ └──SpringContextHolder.java├──GlobalProperties.java└──GlobalConfig.java└── resources└── META-INF└── spring.factories增加配置类用于绑定外部配置(properties和YAML)到Java对象的的一种机制
Data
ConfigurationProperties(GlobalProperties.PREFIX)
public class GlobalProperties {/*** 默认前缀*/public static final String PREFIX encryption.type;/*** 加解密算法*/private String algorithmType;/*** 加解密key值*/private String key;
}注解修改
Target(ElementType.METHOD)
Retention(RetentionPolicy.RUNTIME)
public interface SecuritySupport {/*** 项目默认加解密实现类encryAdecryImpl* */String securityHandler() default encryAdecryImpl;}重写Starter默认的加解密方式
Slf4j
Component
public class EncryAdecryImpl implements SecurityHandler {Resourceprivate GlobalProperties globalProperties;private static volatile SM2 sm2;Overridepublic String encrypt(String original) {log.info(【starter】具体加密的数据{},original);return sm2.encryptBase64(original, KeyType.PublicKey);}Overridepublic String decrypt(String original) {String decryptData StrUtil.utf8Str(sm2.decryptStr(original, KeyType.PrivateKey));log.info(【starter】具体解密的数据{},decryptData);return decryptData;}PostConstructOverridepublic void init() {log.info(获取映射的加密算法类型{},globalProperties.getAlgorithmType());//传的是加密算法KeyPair pair SecureUtil.generateKeyPair(globalProperties.getAlgorithmType());byte[] privateKey pair.getPrivate().getEncoded();byte[] publicKey pair.getPublic().getEncoded();sm2 SmUtil.sm2(privateKey, publicKey);}
}