Developing a Custom SpringBoot Starter for Global Encryption/Decryption
This article explains the concepts behind SpringBoot Starters, walks through the creation of a custom starter that provides global request/response encryption and decryption using SM2, shows the project structure, key configuration files, core code, testing steps, and subsequent optimizations.
The purpose of this guide is to introduce SpringBoot Starter concepts, demonstrate how to build a custom starter that adds global encryption/decryption, describe the testing workflow, and suggest optimizations.
SpringBoot Starter Overview
A SpringBoot Starter packages a set of related dependencies to simplify configuration and initialization, enabling rapid development of specific functional modules while promoting reuse and consistency across projects.
Development Process
To inject a custom starter into SpringBoot, create the necessary resources under src/main/resources/META-INF/spring.factories and define the auto‑configuration class.
<dependency>
<groupId>com.xbhog</groupId>
<artifactId>globalValidation-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>The starter project layout looks like:
demo-spring-boot-starter
└── src
└── main
└── java
└── com.xbhog
├── DemoBean.java
└── DemoBeanConfig.java
└── resources
└── META-INF
└── spring.factoriesIn spring.factories register the auto‑configuration class:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.xbhog.DemoBeanConfigExample configuration class:
@Slf4j
@Configuration
public class DemoBeanConfig {
@Bean
public DemoBean getDemo() {
log.info("已经触发了配置类,正在初始化DemoBean...");
return new DemoBean();
}
}
public class DemoBean {
public void getDemo() {
log.info("方法调用成功");
}
}After packaging, import the starter into a test project via Maven and verify its functionality.
Custom Global Encryption/Decryption Starter
The starter is designed to handle POST request bodies using @ComponentScan , RequestBodyAdvice/ResponseBodyAdvice , and custom annotations. It intercepts requests in beforeBodyRead and responses in beforeBodyWrite to perform decryption and encryption respectively.
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class
> 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());
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());
}The default encryption uses the SM2 algorithm from the Hutool library. An issue with random key generation was solved by initializing the key pair once in a @PostConstruct method.
@PostConstruct
public 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);
}The starter also provides a configurable SecuritySupport annotation to specify a custom SecurityHandler implementation.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SecuritySupport {
String securityHandler() default "securityHandlerImpl";
String exceptionResponse() default "";
}Testing
In a test application, add the starter dependency and invoke the encrypted endpoint. The logs confirm successful encryption/decryption.
@RestController
public class BasicController implements ApplicationContextAware {
@Resource(name = "demoSecurityHandlerImpl")
private SecurityHandler encryAdecry;
private ApplicationContext applicationContext;
@SecuritySupport
@PostMapping("/hello")
public String hello(@RequestBody String name) {
return "Hello " + name;
}
@GetMapping("/configTest")
public String configTest(@RequestParam("name") String name) {
return encryAdecry.encrypt(name);
}
}Optimization
The project structure was refined, a GlobalProperties class was added for external configuration binding, and the default encryption implementation was updated to initialize the SM2 key pair based on a configurable algorithm type.
@Data
@ConfigurationProperties(GlobalProperties.PREFIX)
public class GlobalProperties {
public static final String PREFIX = "encryption.type";
private String algorithmType;
private String key;
}Finally, the custom starter can be published to a Maven repository for reuse across multiple services.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.