☰
Java EE 进阶:6.SpringBoot 配置文件(第二弹)
2026/11/3 2:49:21 网站建设 项目流程

目录

6.综合性练习

6.1 验证码案例

6.1.1 需求

6.1.2 准备工作

6.1.3 约定前后端交互接口

需求分析

接口定义

1.生成验证码

2.校验验证码是否正确

6.1.4 Hutool工具介绍

6.1.5 实现服务端代码

1.引入依赖

2.实现验证码

测试环节

3.校验验证码

6.1.6 调整前端页面代码

6.1.7 运行测试

小结

7.总结(课件内容)

8.代码分享


书接上文:Java EE 进阶:6.SpringBoot 配置文件(第一弹)~~

6.综合性练习

6.1 验证码案例

随着安全性的要求越来越高, 目前项目中很多都使用了验证码, 验证码的形式也是多种多样, 更复杂的图形验证码和行为验证码已经成为了更流行的趋势.

验证码其实并不难,像上面的验证码就是:验证码=字符串+干扰元素

字符串:0~9、a~z、A~Z、汉字……

干扰元素:x 轴 y 轴 任意两点之间随便连个直线、曲线、圆形……

像下面这些验证码更是简单,搞个图片集,在 x 轴上就可以操作完成👇

验证码的实现方式很多, 可以前端实现, 也可以后端实现. 网上也有比较多的插件或者工具包可以使用,咱们选择使用 Hutool 提供的小工具来实现

而且现在前后端也不怎么去写验证码了,因为现在验证码都是开源的了,人家都写好了,咱们直接拿来调用就行了~~

比如:https://gitee.com/LongbowEnterprise/SliderCaptcha#http://longbowenterprise.gitee.io/slidercaptcha/

我们这里就主要是 Hutool 去弄了:简介 | Hutool

6.1.1 需求

界面如下图所示
1. 页面生成验证码
2. 输入验证码, 点击提交, 验证用户输入验证码是否正确, 正确则进行页面跳转

6.1.2 准备工作

创建项目,引入 SpringMVC 的依赖包,把前端页面放在项目中(课件中提供)

码云地址:JavaEE进阶课程资料包

此时我们运行程序发现页面是这样的👇

6.1.3 约定前后端交互接口

需求分析

后端需要提供两个服务
1. 生成验证码, 并返回验证码
2. 校验验证码是否正确

接口定义
1.生成验证码

请求:

请求URL: /captcha/getCaptcha

响应: 验证码图片内容
浏览器给服务器发送一个 /captcha/getCaptcha 这样的请求, 服务器返回一个图片, 浏览器显
示在页面上

2.校验验证码是否正确

请求: /captcha/check

请求URL: /captcha/check 请求参数: captcha=xn8d

captcha : 用户输入的验证码
响应:

true

根据用户输入的验证码, 校验验证码是否正确. true: 验证成功. false: 验证失败.

6.1.4 Hutool工具介绍

咱们课程中验证码的实现, 使用Hutool提供的小工具来实现
Hutool是一个Java工具包类库,对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装,组成各种Util工具类.
Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以"甜甜的".


Hutool官网: Hutool🍬一个功能丰富且易用的Java工具库,涵盖了字符串、数字、集合、编码、日期、文件、IO、加密、数据库JDBC、JSON、HTTP客户端等功能。
Hutool参考文档: Hutool参考文档
Hutool源码: GitHub - chinabugotech/hutool: 🍬A set of tools that keep Java sweet. · GitHub

6.1.5 实现服务端代码

1.引入依赖

<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-captcha</artifactId> <version>5.8.22</version> </dependency>

然后把 hutool-http 改为 hutool-captcha👇

注意:这里一开始没引入版本的时候是报错的,博主这里引入版本号之后又删下去了才不报错~~

接下来需要给它版本号,因为这个并没有被 SpringBoot 集成,所以版本号需要我们去写,但是我们并不知道版本号是啥,这时候就需要去 Maven 仓库中去查了

当然,我们也可以在 API 这里直接看到👇

可以看到版本号是 5.8.48👇

加入版本号,依赖就引入完成了👇

2.实现验证码
测试环节

直接使用它的代码进行测试👇

根据API生成验证码, 并进行测试

package com.bit.captcha; import cn.hutool.captcha.CaptchaUtil; import cn.hutool.captcha.LineCaptcha; /** * Created with IntelliJ IDEA. * Description: * User: CoderYanger * Date: 2026-11-01 * Time: 16:10 */ public class CaptchaTest { public static void main(String[] args) { //定义图形验证码的长和宽 LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100); //图形验证码写出,可以写出到文件,也可以写出到流 lineCaptcha.write("d:/line.png"); } }

运行成功👇

这时我们根据它的输出路径,在 D 盘中就能找到刚刚生成的图片👇

这就生成完毕了,用起来非常简单~~

当然,我们也可以获取到它的 code👇

我们改为 打印👇

public class CaptchaTest { public static void main(String[] args) { //定义图形验证码的长和宽 LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100); //图形验证码写出,可以写出到文件,也可以写出到流 lineCaptcha.write("d:/line.png"); //输出code System.out.println(lineCaptcha.getCode()); } }

再生成一次👇


验证码项目演示-写到浏览器中

上述视频中生成的验证码对于前端页面有点大,我们适当调整一下👇

ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(100, 50);

但是这就属于硬编码了,不太方便,我们接下来尝试将其放到配置文件中~~

验证码配置项对应的Java对象👇

package com.bit.captcha.model; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; /** * Created with IntelliJ IDEA. * Description: * User: CoderYanger * Date: 2026-11-01 * Time: 17:29 */ @ConfigurationProperties(prefix = "captcha") @Configuration @Data public class CaptchaProperties { private Integer width; private Integer height; }

配置文件👇

#spring.application.name=spring-captcha-demo #将上面 properties 文件中的默认内容自行转成下面的 spring: application: name: spring-captcha-demo captcha: width: 100 height: 50

调整Controller代码👇

package com.bit.captcha.controller; import cn.hutool.captcha.CaptchaUtil; import cn.hutool.captcha.ShearCaptcha; import com.bit.captcha.model.CaptchaProperties; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; /** * Created with IntelliJ IDEA. * Description: * User: CoderYanger * Date: 2026-11-01 * Time: 16:58 */ @RequestMapping("/captcha") @RestController public class CaptchaController { @Autowired//把配置文件的内容注入进来 private CaptchaProperties captchaProperties; @RequestMapping("/getCaptcha") public void getCaptcha(HttpServletResponse response){ try { //生成验证码 ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(captchaProperties.getWidth(),captchaProperties.getHeight()); //此时我们想把生成的验证码写到浏览器中,需要 response captcha.write(response.getOutputStream()); } catch (IOException e) { throw new RuntimeException(e); }//我们此时来运行试试效果~~ } }
3.校验验证码
package com.bit.captcha.controller; import cn.hutool.captcha.CaptchaUtil; import cn.hutool.captcha.ShearCaptcha; import com.bit.captcha.model.CaptchaProperties; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; /** * Created with IntelliJ IDEA. * Description: * User: CoderYanger * Date: 2026-11-01 * Time: 16:58 */ @RequestMapping("/captcha") @RestController public class CaptchaController { @Autowired//把配置文件的内容注入进来 private CaptchaProperties captchaProperties; private String code; @RequestMapping("/getCaptcha") public void getCaptcha(HttpServletResponse response){ try { //生成验证码 ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(captchaProperties.getWidth(),captchaProperties.getHeight()); code=captcha.getCode(); //此时我们想把生成的验证码写到浏览器中,需要 response captcha.write(response.getOutputStream()); } catch (IOException e) { throw new RuntimeException(e); }//我们此时来运行试试效果~~ } @RequestMapping("/check") public boolean check(String captcha){ if(code.equals(captcha)) } }

但是写到这里就出现问题了,我们知道 HttpServletResponse 是无状态的,那么在多个用户访问的时候就会出现问题,当张三来访问的时候 code=1234,然后李四也来访问了,此时 code又变成了 5678,此时张三在 check 方法部分输入 1234,就显示错误了~~

当时我们是怎么解决这个问题的??我们为了去保存用户信息,去存储不同用户不同的值,使用的是 session~~

所以我们可以把 code 放到 session 中,代码就变成了👇

code=captcha.getCode(); //存储到session中 session.setAttribute("key",code); //同时也要记录时间,时间过去太久就不校验了,直接返回 false session.setAttribute("date",new Data());

那么此时对于 时间 和 code 又是硬编码了,所以我们还是使用配置文件👇

#spring.application.name=spring-captcha-demo #将上面 properties 文件中的默认内容自行转成下面的 spring: application: name: spring-captcha-demo captcha: width: 150 height: 50 session: key: CAPTCHA_SESSION_KEY date: CAPTCHA_SESSION_DATE

验证码配置项对应的Java对象👇

@ConfigurationProperties(prefix = "captcha") @Configuration @Data public class CaptchaProperties { private Integer width; private Integer height; private Session session; @Data public static class Session{//直接使用内部类的方式 private String key; private String date; } }

此时的 Controller 代码👇

package com.bit.captcha.controller; import cn.hutool.captcha.CaptchaUtil; import cn.hutool.captcha.ShearCaptcha; import com.bit.captcha.model.CaptchaProperties; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; import java.util.Date; /** * Created with IntelliJ IDEA. * Description: * User: CoderYanger * Date: 2026-11-01 * Time: 16:58 */ @RequestMapping("/captcha") @RestController public class CaptchaController { @Autowired//把配置文件的内容注入进来 private CaptchaProperties captchaProperties; //定义过期时间 private final static Long VALID_TIME=30*60*1000L;//30min private String code; //生成验证码 @RequestMapping("/getCaptcha") public void getCaptcha(HttpSession session,HttpServletResponse response){ try { //生成验证码 ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(captchaProperties.getWidth(),captchaProperties.getHeight()); code=captcha.getCode(); //存储到session中 session.setAttribute(captchaProperties.getSession().getKey(),code); //同时也要记录时间,时间过去太久就不校验了,直接返回 false session.setAttribute(captchaProperties.getSession().getDate(),new Date()); //此时我们想把生成的验证码写到浏览器中,需要 response captcha.write(response.getOutputStream()); } catch (IOException e) { throw new RuntimeException(e); } } /** * 验证用户输入的验证码是否正确 * @param captcha:用户输入的验证码 * @param session * @return false:校验失败,true:验证成功 */ @RequestMapping("/check") public boolean check(String captcha,HttpSession session){ //这边就可以从session中去取了 if(!StringUtils.hasLength(captcha)){ return false; } //验证 验证码 String code=(String)session.getAttribute(captchaProperties.getSession().getKey()); Date date=(Date)session.getAttribute(captchaProperties.getSession().getDate()); //验证码正确且没有过期,具体逻辑需要看需求 if(captcha.equalsIgnoreCase(code)&&date!=null&& System.currentTimeMillis()-date.getTime()<VALID_TIME){ return true; } return false; } }

测试后端代码👇发现没有问题

验证码项目-后端代码测试

答疑:为什么配置文件上写上👇就报错??

null: value: ~

而写成👇就不报错??

null.value: ~

其实上述报错日志写的很清晰了,那是因为 null 是一个关键字,而合在一起写的时候,就认为 null.value 这个整体是 key 了,所以不报错~~


其实上述后端代码运行的时候,咱们通过 Fiddler 抓包就能发现这是一个 .PNG 文件👇

而这正是浏览器自动正确解析的结果,但是并不是每个浏览器都能够正确解析的,所以我们一般情况下为了确保正确解析出来,会在代码中设置 ContentType,我们希望它返回的是一个图片,注意,这这些代码我们一般给写在最上面(你使用 response 之前),而不是最下面,否则不生效的👇

//生成验证码 @RequestMapping("/getCaptcha") public void getCaptcha(HttpSession session,HttpServletResponse response){ response.setContentType("image/jpeg");//设置希望返回的类型 //response.setCharacterEncoding("utf-8");//设置编码格式也在这里 try { //…… }

6.1.6 调整前端页面代码

修改 index.html👇

<img id="verificationCodeImg" src="/admin/captcha" style="cursor: pointer;" title="看不清?换一张" />

这一行的 URL 可以是绝对路径,也可以是相对路径,或者是网络路径,而此时我们后端生成的图片就是以网络路径显示的,所以此处改为后端对应的网络路径👇

<img id="verificationCodeImg" src="/captcha/getCaptcha" style="cursor: pointer;" title="看不清?换一张" />

启动服务后,重新访问,此时我们的图片就能正常显示了👇

但是这里会有个问题:我要是连续点两次刷新的话,这个图片不会变化,但实际上我们希望它是跟着刷新两次的,这是为什么??

这是因为浏览器会有一些缓存,我们需要去做一些禁止缓存的东西,咱们使用的是 get 请求,这就涉及到 get 请求和 post 请求的区别了,理论上 get 请求是 幂等性的,因为它是从浏览器中获取资源,我们认为获取资源是不会发生变化的,所以浏览器会进行缓存,而如果它认为资源没有发生变化,那么这时候它可能就不请求了~~

关于这里的处理有两种方式:前端处理 和 后端处理

后端处理👇

response.setContentType("image/jpeg");//设置希望返回的类型 //一般情况下,这个处理是前端做,后端不做 response.setHeader("Progma","No-cahce");//加上这一行 // response.setCharacterEncoding("utf-8");//设置编码格式

前端处理👇网上随便搜一下:meta progam nocahe,然后加上下面的代码

<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Expires" content="0" />

补充 ajax 代码,点击提交按钮,发送请求去服务端进行校验

$("#checkCaptcha").click(function () { $.ajax({ type:"post", url:"/captcha/check", data:{ captcha:$("#inputCaptcha").val() }, success:function (result){ if(result){ location.href="success.html"; }else{ alert("验证码错误,请重新输入"); } } });

6.1.7 运行测试

验证码项目运行测试

在前端代码中,这里还有一个知识点给大家提一下👇

$("#verificationCodeImg").click(function(){ $(this).hide().attr('src', '/captcha/getCaptcha?dt=' + new Date().getTime()).fadeIn(); });

给这个图片加了一个点击事件,就是我每次点击的时候希望它去请求我们的验证码

后面跟了一个参数 dt,给这个 dt 赋值了一个当前的时间,其目的也是为了防止缓存的,上述加完👇只是说页面不缓存了

<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Expires" content="0" />

但是这里却是针对每次请求的,每次的 dt 都不一样,浏览器一看第一次 dt 为1,第二次 dt 为2……就不敢缓存了,所以说防止浏览器缓存的方式有很多种~~

答疑:为什么后端代码中的 Session 内部类要加上 static??

那我们把 static 去掉试试看👇

@Data public class Session{//直接使用内部类的方式 private String key; private String date; }

启动服务,并刷新页面👇

我们发现这时候它不认识 getKey() 了,找不到了~~

我们如果把这个 Session 类挪出来放在外面的话,不加 static 👇

@Data public class Session { private String key; private String date; }

我们发现它是可以运行的,页面也没有问题👇

这就说明写在内部类的话 static 是不能省略的~~

为什么??因为在这里👇

@Configuration 表示我们把这个对象交给 Spring 进行管理了,那 Spring 就会帮我们 new 这个对象了,但是如果是普通内部类的话,Spring 是 new 不了的,赋值时是拿不了的,但是加上 static 之后那就跟 Spring 没关系了,只是写在这里一样而已,这个内部类跟在外面就一样了

这也就说明我们的内部类是不能交给 Spring 进行管理的,但是静态内部类可以,这也是我们加上 static 的原因

简单点说就是,普通内部类不能 IoC,但是静态内部类可以~~

小结

配置项可以是第三方的,也可以是自定义的

配置项分为两种格式:properties 和 yml

properties:单词和单词之间使用点来分割,key 和 value 之间使用等号分割,类型不用管,直接去写,后端使用不同类型去接收

yml:格式是 key: value,这个空格不可省略,单词和单词之间使用冒号+换行去分割,并且 key 不能重复,相同 key 必须放在一起,且各个级别要对齐

yml 可以和 properties 共存,但一个项目中建议使用一种配置类型文件.

7.总结(课件内容)

1. properties 是以 key=value 的形式配置的键值类型的配置文件, yml 使用的是树形配置方式.
2. 读取配置文件内容, 使用 @Value 注解, 注解内使用" ${} "的格式读取.
3. yml 层级之间使用换行缩进的方式配置, key 和 value 之间使用": "(英文冒号)加空格的方式设置, 并且空格不可省略.
4. properties 为早期并且默认的配置文件格式, 其配置存在一定的冗余数据, 使用 yml 可以很好的解决数据冗余的问题, 但不适合复杂配置.
5. yml 可以和 properties 共存,但一个项目中建议使用一种配置类型文件.

8.代码分享

CaptchaController.java

package com.bit.captcha.controller; import cn.hutool.captcha.CaptchaUtil; import cn.hutool.captcha.ShearCaptcha; import com.bit.captcha.model.CaptchaProperties; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; import java.util.Date; /** * Created with IntelliJ IDEA. * Description: * User: CoderYanger * Date: 2026-11-01 * Time: 16:58 */ @RequestMapping("/captcha") @RestController public class CaptchaController { @Autowired//把配置文件的内容注入进来 private CaptchaProperties captchaProperties; //定义过期时间 private final static Long VALID_TIME=30*60*1000L;//30min private String code; //生成验证码 @RequestMapping("/getCaptcha") public void getCaptcha(HttpSession session,HttpServletResponse response){ response.setContentType("image/jpeg");//设置希望返回的类型 //一般情况下,这个处理是前端做,后端不做 response.setHeader("Progma","No-cahce"); // response.setCharacterEncoding("utf-8");//设置编码格式 try { //生成验证码 ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(captchaProperties.getWidth(),captchaProperties.getHeight()); code=captcha.getCode(); //存储到session中 session.setAttribute(captchaProperties.getSession().getKey(),code); //同时也要记录时间,时间过去太久就不校验了,直接返回 false session.setAttribute(captchaProperties.getSession().getDate(),new Date()); //此时我们想把生成的验证码写到浏览器中,需要 response captcha.write(response.getOutputStream()); } catch (IOException e) { throw new RuntimeException(e); } } /** * 验证用户输入的验证码是否正确 * @param captcha:用户输入的验证码 * @param session * @return false:校验失败,true:验证成功 */ @RequestMapping("/check") public boolean check(String captcha,HttpSession session){ //这边就可以从session中去取了 if(!StringUtils.hasLength(captcha)){ return false; } //验证 验证码 String code=(String)session.getAttribute(captchaProperties.getSession().getKey()); Date date=(Date)session.getAttribute(captchaProperties.getSession().getDate()); //验证码正确且没有过期,具体逻辑需要看需求 if(captcha.equalsIgnoreCase(code)&&date!=null&& System.currentTimeMillis()-date.getTime()<VALID_TIME){ return true; } return false; } }

CaptchaProperties.java

package com.bit.captcha.model; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; /** * Created with IntelliJ IDEA. * Description: * User: CoderYanger * Date: 2026-11-01 * Time: 17:29 */ @ConfigurationProperties(prefix = "captcha") @Configuration @Data public class CaptchaProperties { private Integer width; private Integer height; private Session session; @Data public static class Session{//直接使用内部类的方式 private String key; private String date; } }

application.yml

#spring.application.name=spring-captcha-demo #将上面 properties 文件中的默认内容自行转成下面的 spring: application: name: spring-captcha-demo captcha: width: 150 height: 50 session: key: CAPTCHA_SESSION_KEY date: CAPTCHA_SESSION_DATE #null.value: ~ #null: # value: ~

index.html

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Expires" content="0" /> <title>验证码</title> <style> #inputCaptcha { height: 30px; vertical-align: middle; } #verificationCodeImg{ vertical-align: middle; } #checkCaptcha{ height: 40px; width: 100px; } </style> </head> <body> <h1>输入验证码</h1> <div id="confirm"> <input type="text" name="inputCaptcha" id="inputCaptcha"> <img id="verificationCodeImg" src="/captcha/getCaptcha" style="cursor: pointer;" title="看不清?换一张" /> <input type="button" value="提交" id="checkCaptcha"> </div> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script> <script> $("#verificationCodeImg").click(function(){ $(this).hide().attr('src', '/captcha/getCaptcha?dt=' + new Date().getTime()).fadeIn(); }); $("#checkCaptcha").click(function () { $.ajax({ type:"post", url:"/captcha/check", data:{ captcha:$("#inputCaptcha").val() }, success:function (result){ if(result){ location.href="success.html"; }else{ alert("验证码错误,请重新输入"); } } }); }); </script> </body> </html>

success.html

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>验证成功页</title> </head> <body> <h1>验证成功</h1> </body> </html>

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询