版本:0.4.0 日期:2025-08-31
框架概述
本框架集成第三开源工具,开发Spring Boot Starter风格的包,方便引入及使用
主要目标:
-
只集成工具,适当的扩展工具,使其更容易与Spring Boot集成
-
每个Starter功能单一,或者只专注某一方面,如:ID生成器,验证码生成器等等
-
积累知识,技术,经验
主要依赖及版本对应
-
JDK: 17.0.12
-
Spring Boot 3.0.13
本框架版本 | Spring Boot 版本 |
---|---|
< = 0.4.0 |
3.0.13 |
如何使用
有两个方法引入本框架,分别是继承`starter-parent`项目和导入`dependencies`项目。第一个方法更方便;第二个方法更有灵活性。
如果是示例或小型系统(单一项目),推荐使用第一种方法。大型系统(多项目)就使用第二种方法 |
继承`starter-parent`项目
`starter-parent`继承了`spring-boot-starter-parent`项目,在此基础上扩展功能,如:导入本框架,添加插件等等
在项目`pom.xml`中添加:
<parent>
<groupId>io.gitee.yunjiao-source.spring-boot</groupId>
<artifactId>starter-parent</artifactId>
<version>3.0.13</version>
</parent>
`starter-parent`的版本与Spring Boot的版本保持一致。对应本框架的版本,请查看主要依赖及版本对应 |
导入`dependencies`项目
`dependencies`项目包含本框架的所有包,在项目中导入即可
在项目`pom.xml`中添加:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.gitee.yunjiao-source.spring-boot</groupId>
<artifactId>dependencies</artifactId>
<version>0.4.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
编译源码
源码目录中包含多个独立项目,需要分别编译,打包
因为是独立的项目,IDE中只能自动导入主项目,其他项目需要手工导入 |
目录结构
项目名 | 目录及说明 |
---|---|
Spring Boot |
autoconfigure(自动配置包), dependencies(所有的依赖包), extensions(扩展包), starters(启动器包) |
Spring Boot :: Starter Parent |
starter-parent, 此项目集成`Spring Boot`的`spring-boot-starter-parent`项目 |
Spring Boot :: Examples |
examples, 示例项目 |
Spring Boot :: Projects |
projects, 示例应用 |
编译打包
首先编译打包主项目
# cd yunjiao-spring-boot
# mvn clean install
其次打包starter-parent
# cd starter-parent
# mvn clean install
其他的项目,编译,打包方法是一样的
ID扩展
ID生成是企业信息系统需要的,重要的,必须的功能。目前使用雪花算法生成ID是最流行的,本框架集成`Hutool`,`uid-generator`等框架, 这些框架实现了雪花算法。
雪花算法需要一个中心化服务分配机器ID,在不同的应用中实现统一的ID(不重复),但这并不常见,实现也很复杂。一般情况下,比如在用户应用与订单应用, 用户表的ID与订单表的ID是可以重复的。
在微服务场景中,需要在多台服务器上部署相同的应用,如用户应用,必须保证每个用户应用新增用户的ID是不重复的。
根据以上分析,可以把每台服务当做中心化服务,分配机器ID。只需要在系统定义环境变量,雪花ID算法工具获取环境变量,就可以保证相同的应用统一的ID(不重复), 这个思路我称为`有限的分布式ID`。 本框架实现了`有限的分布式ID`, 支持在系统中定义环境变量`SNOWFLAKE_WORKER_ID`和`SNOWFLAKE_DATACENTER_ID`。
如果需要真正的分布式ID,推荐`uid-generator-starter` ( 官网 ), 它支持中心化服务,如 数据库,redis, zk等 |
如何使用
在pom.xml文件中添加
<dependency>
<groupId>io.gitee.yunjiao-source.spring-boot</groupId>
<artifactId>starter-id</artifactId>
</dependency>
配置指南
在`application.yml`中配置参数
配置参数 | 默认值 | 说明 |
---|---|---|
spring.id.uid-generator.time-bits |
28 |
|
spring.id.uid-generator.worker-bits |
22 |
|
spring.id.uid-generator.seq-bits |
13 |
|
spring.id.uid-generator.epoch-str |
2025-08-20 |
开发指南
在服务中注入
@Slf4j
@Component
public class IdCommandLineRunner implements CommandLineRunner {
@Autowired
private Snowflake snowflake;
@Autowired
private UidGeneratorCached uidGeneratorCached;
@Autowired
private UidGeneratorDefault uidGeneratorDefault;
@Override
public void run(String... args) throws Exception {
TimeUnit.SECONDS.sleep(3);
log.info("Snowflake = {}", generator(() -> snowflake.nextId()).size());
log.info("uidGeneratorCached = {}", generator(() -> uidGeneratorCached.getUID()).size() );
log.info("uidGeneratorDefault = {}", generator(() -> uidGeneratorDefault.getUID()).size() );
}
private Set<Long> generator(Supplier<Long> supplier) {
return IntStream.range(0, 4000).mapToObj(i -> supplier.get())
.peek(l -> System.out.print(l + ","))
.collect(Collectors.toSet());
}
}
在使用中,日志中会出现警告信息
WARN --- [ main] y.s.e.i.u.EnvironmentWorkerIdAssigner : Uid-Generator 框架雪花算法配置使用默认参数。如需支持分布式,请设置系统环境变量:SNOWFLAKE_WORKER_ID
WARN --- [ main] y.s.a.id.HutoolIdConfiguration : Hutool 框架雪花算法配置使用默认参数。如需支持分布式,请设置系统环境变量:SNOWFLAKE_WORKER_ID 与 SNOWFLAKE_DATACENTER_ID
如果不是微服务或集群部署应用,可以忽略此警告
本框架提供的雪花ID工具,没有必要都使用,使用其中的一种就可以满足大部分场景 |
使用`CachedUidGeneratorConfigurer`自定义配置
@Slf4j
@Configuration
public class IdConfiguration {
@Bean
CachedUidGeneratorConfigurer CachedUidGeneratorConfigurer() {
return generator -> {
log.info("自定义配置");
generator.setBoostPower(3);
generator.setPaddingFactor(50);
generator.setScheduleInterval(60);
};
}
}
Captcha(验证码)扩展
Captcha(验证码)是很常见的需求,广泛应用在登录,下载等功能中。本框架集成Hutool,aj-captcha等开源验证码工具,提供多种验证码,如:线段干扰验证码, 圆圈干扰验证码,扭曲干扰验证码,GIF验证码,滑块拼图验证码,文字点选验证码,旋转拼图验证码等等
本框架验证码服务只有生成,校验功能。不涉及验证码缓存及失效 |
如何使用
在pom.xml文件中添加
<dependency>
<groupId>io.gitee.yunjiao-source.spring-boot</groupId>
<artifactId>starter-captcha</artifactId>
</dependency>
配置指南
在`application.yml`中配置参数
线段干扰验证码(line)配置
配置参数 | 默认值 | 说明 |
---|---|---|
spring.captcha.hutool.line.width |
250 |
图片的宽度 |
spring.captcha.hutool.line.height |
50 |
图片的高度 |
spring.captcha.hutool.line.interfereCount |
60 |
验证码干扰元素个数 |
spring.captcha.hutool.line.backgroundColor |
ColorType.white |
背景色 |
spring.captcha.hutool.line.transparency |
null |
文字透明度,取值0~1,1表示不透明 |
spring.captcha.hutool.line.fuzziness |
2 |
模糊度(0 - 30) |
spring.captcha.hutool.line.validIgnoreCase |
true |
校验时忽略大小写 |
spring.captcha.hutool.line.font.name |
null |
字体名称, 为空表示使用系统默认字体 |
spring.captcha.hutool.line.font.style |
FontStyle.plain |
字体风格 |
spring.captcha.hutool.line.font.size |
36 |
字体大小 |
spring.captcha.hutool.line.code.generator |
CodeGeneratorType.numAndChar |
码生成类型 |
spring.captcha.hutool.line.code.length |
5 |
字符长度 |
圆圈干扰验证码(circle)配置
配置参数 | 默认值 | 说明 |
---|---|---|
spring.captcha.hutool.circle.width |
250 |
图片的宽度 |
spring.captcha.hutool.circle.height |
50 |
图片的高度 |
spring.captcha.hutool.circle.interfereCount |
30 |
验证码干扰元素个数 |
spring.captcha.hutool.circle.backgroundColor |
ColorType.white |
背景色 |
spring.captcha.hutool.circle.transparency |
null |
文字透明度,取值0~1,1表示不透明 |
spring.captcha.hutool.circle.fuzziness |
2 |
模糊度(0 - 30) |
spring.captcha.hutool.circle.validIgnoreCase |
true |
校验时忽略大小写 |
spring.captcha.hutool.circle.font.name |
null |
字体名称, 为空表示使用系统默认字体 |
spring.captcha.hutool.circle.font.style |
FontStyle.plain |
字体风格 |
spring.captcha.hutool.circle.font.size |
36 |
字体大小 |
spring.captcha.hutool.circle.code.generator |
CodeGeneratorType.numAndChar |
码生成类型 |
spring.captcha.hutool.circle.code.length |
5 |
字符长度 |
扭曲干扰验证码(shear)配置
配置参数 | 默认值 | 说明 |
---|---|---|
spring.captcha.hutool.shear.width |
250 |
图片的宽度 |
spring.captcha.hutool.shear.height |
50 |
图片的高度 |
spring.captcha.hutool.shear.interfereCount |
10 |
干扰线宽度 |
spring.captcha.hutool.shear.backgroundColor |
ColorType.white |
背景色 |
spring.captcha.hutool.shear.transparency |
null |
文字透明度,取值0~1,1表示不透明 |
spring.captcha.hutool.shear.fuzziness |
2 |
模糊度(0 - 30) |
spring.captcha.hutool.shear.validIgnoreCase |
true |
校验时忽略大小写 |
spring.captcha.hutool.shear.font.name |
null |
字体名称, 为空表示使用系统默认字体 |
spring.captcha.hutool.shear.font.style |
FontStyle.plain |
字体风格 |
spring.captcha.hutool.shear.font.size |
36 |
字体大小 |
spring.captcha.hutool.shear.code.generator |
CodeGeneratorType.numAndChar |
码生成类型 |
spring.captcha.hutool.shear.code.length |
5 |
字符长度 |
Gif验证码(gif)配置
配置参数 | 默认值 | 说明 |
---|---|---|
spring.captcha.hutool.gif.width |
250 |
图片的宽度 |
spring.captcha.hutool.gif.height |
50 |
图片的高度 |
spring.captcha.hutool.gif.interfereCount |
10 |
干扰线宽度 |
spring.captcha.hutool.gif.backgroundColor |
ColorType.white |
背景色 |
spring.captcha.hutool.gif.transparency |
null |
文字透明度,取值0~1,1表示不透明 |
spring.captcha.hutool.gif.fuzziness |
2 |
模糊度(0 - 30) |
spring.captcha.hutool.gif.validIgnoreCase |
true |
校验时忽略大小写 |
spring.captcha.hutool.gif.font.name |
null |
字体名称, 为空表示使用系统默认字体 |
spring.captcha.hutool.gif.font.style |
FontStyle.plain |
字体风格 |
spring.captcha.hutool.gif.font.size |
36 |
字体大小 |
spring.captcha.hutool.gif.code.generator |
CodeGeneratorType.numAndChar |
码生成类型 |
spring.captcha.hutool.gif.code.length |
5 |
字符长度 |
spring.captcha.hutool.gif.quality |
10 |
量化器取样间隔, 1 ~ 20 值之间 - 默认是10ms |
spring.captcha.hutool.gif.repeat |
0 |
帧循环次数,默认是 0, 意味着无限循环 |
spring.captcha.hutool.gif.min-color |
0 |
设置随机颜色时,最小的取色范围 |
spring.captcha.hutool.gif.max-color |
255 |
设置随机颜色时,最大的取色范围 |
滑块拼图验证码(blockPuzzle)及文字点选验证码(clickWord)配置
配置参数 | 默认值 | 说明 |
---|---|---|
spring.captcha.anji.water-mark |
我的水印 |
右下角水印文字 |
spring.captcha.anji.water-font |
WenQuanZhengHei.ttf |
右下角水印字体 |
spring.captcha.anji.block-puzzle.jigsaw |
滑动拼图底图路径 |
|
spring.captcha.anji.block-puzzle.slip-offset |
5 |
校验滑动拼图允许误差偏移量 |
spring.captcha.anji.block-puzzle.interference-options |
0 |
滑块干扰项 |
spring.captcha.anji.click-word.pic-click |
点选文字底图路径 |
|
spring.captcha.anji.click-word.font-type |
WenQuanZhengHei.ttf |
点选文字验证码的文字字体 |
spring.captcha.anji.click-word.font-style |
FontStyle.bold |
点选字体样式 |
spring.captcha.anji.click-word.font-size |
25 |
点选字体大小 |
spring.captcha.anji.click-word.click-word-count |
4 |
点选文字个数 |
spring.captcha.anji.click-word.slip-offset |
13 |
校验拼图允许误差(默认13像素). |
开发指南
使用验证码服务工厂(CaptchaServiceFactory),可以很方便获取验证码服务
@Slf4j
@Component
public class CaptchaCommandLineRunner implements CommandLineRunner {
@Autowired
private CaptchaServiceFactory factory;
@Override
public void run(String... args) throws Exception {
CaptchaService circleService = factory.findService(CaptchaCategory.circle);
CaptchaData data = circleService.draw();
log.info(GsonUtils.toJson(data));
saveImage(data);
CaptchaService lineService = factory.findService(CaptchaCategory.line);
data = lineService.draw();
saveImage(data);
CaptchaService gifService = factory.findService(CaptchaCategory.gif);
data = gifService.draw();
saveImage(data);
CaptchaService shearService = factory.findService(CaptchaCategory.shear);
data = shearService.draw();
saveImage(data);
CaptchaService blockPuzzleService = factory.findService(CaptchaCategory.blockPuzzle);
data = blockPuzzleService.draw();
saveImage(data);
CaptchaService clickWordService = factory.findService(CaptchaCategory.clickWord);
data = clickWordService.draw();
saveImage(data);
}
private void saveImage(CaptchaData data) throws IOException {
Path targetDir = Paths.get("target", "processed-images");
if (!Files.exists(targetDir)) {
Files.createDirectories(targetDir);
}
Path filePath = targetDir.resolve(data.category().name() + "-captcha." + data.category().getExt());
Files.write(filePath, data.captchaImage());
if (data.backgroundImage() != null) {
filePath = targetDir.resolve(data.category().name() + "-background." + data.category().getExt());
Files.write(filePath, data.backgroundImage());
}
}
}
所有的验证码服务生成的验证码信息是统一的,使用更加方便
验证码信息(json)格式
{"key":"5e1bf15c178b4ea3a6d0599ba8fb3743","captchaImage":[-119,80,78,71,13,10,....],"backgroundImage":null,"code":"z8yOk","category":"circle"}
验证码信息属性说明
属性 | 说明 |
---|---|
key |
String类型,验证码ID,生成的每个验证码都有唯一的ID |
captchaImage |
byte[]类型,验证码图片。 |
backgroundImage |
byte[]类型,验证码背景图片。分类是blockPuzzle才有值 |
code |
String类型,校验码, |
category |
枚举类型,分类 |
如果需要Base64类型的图片信息,可以调用`CaptchaData.captchaImageBase64Url`或`CaptchaData.backgroundImageBase64Url`方法 |
使用验证码缓存工厂(CaptchaCacheFactory)存储验证码信息,支持`Hutool`框架的`TimedCache`缓存和`Guava`框架的`LoadingCache`缓存,可以自由 选择缓存,而且可以自定义配置
@Slf4j
@Configuration
public class CaptchaConfiguration {
@PostConstruct
void postConstruct() {
// 设置过期时间
CaptchaCacheFactory.initHtoolCache(Duration.ofSeconds(30));
}
}
验证码缓存工厂(CaptchaCacheFactory)功能与 验证码服务工厂(CaptchaServiceFactory)功能没有关联 |
写一个简单的验证码生成及校验接口
@RestController
@RequiredArgsConstructor
public class CaptchaController {
private final CaptchaServiceFactory factory;
private final CaptchaCache cache = CaptchaCacheFactory.getInstance();
@GetMapping("/captcha")
public CaptchaReponse get(@RequestParam(name = "category") CaptchaCategory category) {
CaptchaService service = factory.findService(category);
CaptchaData data = service.draw();
CaptchaReponse reponse = new CaptchaReponse();
reponse.setKey(data.key());
reponse.setCategory(service.getCategory());
reponse.setCaptchaImageBase64(data.captchaImageBase64Url());
reponse.setBackgroundImageBase64(data.backgroundImageBase64Url());
cache.put(data.key(), data.code());
System.out.println("code=" + data.code());
return reponse;
}
@PostMapping("/captcha")
public String post(@RequestBody CaptchaValidate validate) {
String cacheCode = cache.get(validate.getKey()).orElse(null);
if (!StringUtils.hasText(cacheCode)) {
return "验证码校验失败";
}
CaptchaService service = factory.findService(validate.getCategory());
boolean pass = service.verify(cacheCode, validate.getCode());
if (pass) {
return "验证码校验成功";
} else {
return "验证码校验失败";
}
}
}
获取验证码请求
curl --location --request GET 'http://localhost:8080/captcha?category=line' \
--header 'Accept: /' \
--header 'Host: localhost:8080' \
--header 'Connection: keep-alive'
校验验证码请求
curl --location --request POST 'http://localhost:8080/captcha' \
--header 'Content-Type: application/json' \
--header 'Accept: /' \
--header 'Host: localhost:8080' \
--header 'Connection: keep-alive' \
--data-raw '{
"key": "e3d15841b93543f88e2404fc93cd8edb",
"category": "line",
"code": "1kNk9"
}'
*注意*编写生成验证码接口时,不要将校验码(CaptchaData.code属性)返回给前端 |