当前位置: 首页 > article >正文

[Java实战]Spring Security 添加验证码(二十三)

[Java实战]Spring Security 添加验证码(二十三)

在现代的 Web 应用中,验证码是防止恶意攻击(如暴力破解、自动注册等)的重要手段之一。Spring Security 是一个功能强大的安全框架,提供了用户认证、授权等功能。本文将详细介绍如何在 Spring Security 中添加验证码功能,从而进一步增强应用的安全性。

一. 环境准备

  • openJDK 17+:Spring Boot 3 要求 Java 17 及以上。
  • Spring Boot 3.4.5:使用最新稳定版。
  • 构建工具:Maven 或 Gradle(本文以 Maven 为例)。

二、验证码的作用

验证码(CAPTCHA,Completely Automated Public Turing test to tell Computers and Humans Apart)是一种区分用户是人类还是机器人的测试。常见的验证码类型包括:

  • 图片验证码:用户需要识别并输入图片中的字符。
  • 短信验证码:用户需要输入发送到手机的验证码。
  • 邮箱验证码:用户需要输入发送到邮箱的验证码。

验证码的主要作用是:

  • 防止暴力破解:限制非法登录尝试。
  • 防止自动注册:限制恶意用户批量注册账号。
  • 防止垃圾评论:限制自动发布垃圾评论。

三、Spring Security 添加验证码

1. 添加依赖

在 Spring Boot 项目中,添加验证码功能需要一些额外的依赖。首先,确保你的项目中已经添加了 Spring Security 和 Spring Web 的依赖。

<dependencies><!-- Spring Boot Starter Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring Boot Starter Security --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!-- Thymeleaf --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!-- 图片验证码库 --><dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version></dependency><dependency><groupId>jakarta.servlet</groupId><artifactId>jakarta.servlet-api</artifactId><version>6.0.0</version><scope>provided</scope></dependency>
</dependencies>

2. 配置验证码生成器

使用 Kaptcha 库生成图片验证码。首先,配置 Kaptcha 的 Bean。

@Configuration
public class KaptchaConfig {@Beanpublic Producer kaptchaProducer() {Properties properties = new Properties();properties.put("kaptcha.border", "no");properties.put("kaptcha.textproducer.font.color", "black");properties.put("kaptcha.textproducer.char.space", "5");properties.put("kaptcha.image.width", "125");properties.put("kaptcha.image.height", "45");properties.put("kaptcha.textproducer.char.len", "5");properties.put("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");DefaultKaptcha kaptcha = new DefaultKaptcha();kaptcha.setConfig(new Config(properties));return kaptcha;}
}

3. 创建验证码控制器

创建一个控制器,用于生成和显示验证码图片。

@RestController
public class KaptchaController {@Autowiredprivate Producer kaptchaProducer;@GetMapping("/captcha")public void getKaptcha(HttpServletResponse response, HttpSession session) throws IOException, IOException {response.setDateHeader("Expires", 0);response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");response.addHeader("Cache-Control", "post-check=0, pre-check=0");response.setHeader("Pragma", "no-cache");response.setContentType("image/jpeg");String capText = kaptchaProducer.createText();session.setAttribute("captcha", capText);BufferedImage bi = kaptchaProducer.createImage(capText);ServletOutputStream out = response.getOutputStream();ImageIO.write(bi, "jpg", out);try {out.flush();} finally {out.close();}}
}

4. 修改登录页面

在登录页面login.html中添加验证码输入框和验证码图片。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>Login</title>
</head>
<body>
<form th:action="@{/login}" method="post"><div><label>Username: <input type="text" name="username"></label></div><div><label>Password: <input type="password" name="password"></label></div><div><label>Captcha: <input type="text" name="captcha"></label></div><div><img th:src="@{/captcha}" alt="captcha" onclick="this.src='/captcha?'+new Date()" style="cursor:pointer;"></div><div><input type="submit" value="Sign In"></div>
</form>
</body>
</html>

在这里插入图片描述
在这里插入图片描述

5. 修改 SecurityConfig

SecurityConfig 中添加验证码校验逻辑。

@Configuration
@EnableWebSecurity
public class SecurityConfig {@Autowiredprivate CustomUserDetailsService userDetailsService;@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.csrf(csrf -> csrf.disable()).authorizeHttpRequests(authorize -> authorize.requestMatchers("/admin/**").hasRole("ADMIN").requestMatchers("/user/**").hasRole("USER").requestMatchers("/", "/home", "/register", "/captcha").permitAll().anyRequest().authenticated()).formLogin(form -> form.loginPage("/login").permitAll().defaultSuccessUrl("/home", true)).logout(logout -> logout.permitAll()).addFilterBefore(new CaptchaFilter(), UsernamePasswordAuthenticationFilter.class);return http.build();}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {return config.getAuthenticationManager();}

6. 创建验证码过滤器

创建一个自定义过滤器,用于校验验证码。


public class CaptchaFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain) throws ServletException, IOException {// 仅拦截登录请求(POST 方法)if ("POST".equalsIgnoreCase(request.getMethod())&& "/login".equals(request.getRequestURI())) {// 从前端获取验证码参数(根据方案一或二调整名称)String inputCaptcha = request.getParameter("captcha");String sessionCaptcha = (String) request.getSession().getAttribute("captcha");// 校验逻辑if (inputCaptcha == null || inputCaptcha.isEmpty()|| !inputCaptcha.equalsIgnoreCase(sessionCaptcha)) {// 清除旧验证码并记录错误request.getSession().removeAttribute("captcha");request.getSession().setAttribute("captchaError", "验证码错误");response.sendRedirect("/login?error");return;}// 验证通过后清除验证码(避免重复使用)request.getSession().removeAttribute("captcha");}filterChain.doFilter(request, response);}
}

四、高级用法

1. 短信验证码

除了图片验证码,你还可以使用短信验证码。以下是一个简单的实现示例:

添加依赖
<dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>4.5.0</version>
</dependency>
<dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-dysmsapi</artifactId><version>1.1.0</version>
</dependency>
配置短信服务
@Service
public class SmsService {public void sendSms(String phone, String code) {DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "your-access-key-id", "your-access-key-secret");IAcsClient client = new DefaultAcsClient(profile);CommonRequest request = new CommonRequest();request.setMethod(MethodType.POST);request.setDomain("dysmsapi.aliyuncs.com");request.setVersion("2017-05-25");request.setAction("SendSms");request.putQueryParameter("RegionId", "cn-hangzhou");request.putQueryParameter("PhoneNumbers", phone);request.putQueryParameter("SignName", "your-sign-name");request.putQueryParameter("TemplateCode", "your-template-code");request.putQueryParameter("TemplateParam", "{\"code\":\"" + code + "\"}");try {CommonResponse response = client.getCommonResponse(request);System.out.println(response.getData());} catch (ServerException e) {e.printStackTrace();} catch (ClientException e) {e.printStackTrace();}}
}
修改登录页面

在登录页面中添加短信验证码输入框。

<div><label>SMS Captcha: <input type="text" name="smsCaptcha"></label></div>
修改验证码过滤器

在验证码过滤器中添加短信验证码校验逻辑。

public class CaptchaFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {if ("/login".equals(request.getRequestURI())) {String captcha = request.getParameter("captcha");String smsCaptcha = request.getParameter("smsCaptcha");String sessionCaptcha = (String) request.getSession().getAttribute("captcha");String sessionSmsCaptcha = (String) request.getSession().getAttribute("smsCaptcha");if (captcha == null || !captcha.equalsIgnoreCase(sessionCaptcha)) {request.getSession().setAttribute("captchaError", "Invalid captcha");response.sendRedirect("/login");return;}if (smsCaptcha == null || !smsCaptcha.equalsIgnoreCase(sessionSmsCaptcha)) {request.getSession().setAttribute("smsCaptchaError", "Invalid SMS captcha");response.sendRedirect("/login");return;}}filterChain.doFilter(request, response);}
}

五、常见问题与解决方案

1. 验证码不显示

原因:Kaptcha 配置不正确,或服务器未正确返回图片。

解决方案

  • 检查 Kaptcha 的配置是否正确。
  • 确保 KaptchaController 中的 getKaptcha 方法返回正确的图片。

2. 验证码校验失败

原因:验证码输入错误,或验证码已过期。

解决方案

  • 确保用户输入的验证码正确。
  • 检查验证码的有效期是否过期。

3. 短信验证码发送失败

原因:短信服务配置不正确,或网络问题。

解决方案

  • 检查短信服务的配置是否正确。
  • 确保网络连接正常。

六、总结

本文详细介绍了如何在 Spring Security 中添加验证码功能,包括图片验证码和短信验证码。通过合理使用验证码,可以显著增强应用的安全性。希望本文能帮助你更好地理解和使用 Spring Security 添加验证码。

如果你在使用过程中遇到任何问题,欢迎在评论区留言交流。感谢你的阅读,希望这篇文章对你有所帮助!

相关文章:

[Java实战]Spring Security 添加验证码(二十三)

[Java实战]Spring Security 添加验证码&#xff08;二十三&#xff09; 在现代的 Web 应用中&#xff0c;验证码是防止恶意攻击&#xff08;如暴力破解、自动注册等&#xff09;的重要手段之一。Spring Security 是一个功能强大的安全框架&#xff0c;提供了用户认证、授权等功…...

万文c++继承

1、继承的概念与定义 1.1继承的概念 继承&#xff1a;是c代码复用的手段&#xff0c;允许在原有的基础上扩展&#xff0c;在此之前都是函数层次的复用&#xff0c;继承是类设计层次的复用。 下面有两个类Student和Teacher都有姓名/地址/电话/年龄等成员变量。都有identity身…...

HTTP GET报文解读

考虑当浏览器发送一个HTTP GET报文时&#xff0c;通过Wireshark 俘获到下列ASCII字符串&#xff1a; GET /cs453/index.html HTTP/1.1 Host: gaia.cs.umass.edu User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.2) Gecko/20040804 Netscape/7.2 (ax) Acc…...

Linux grep -r 查找依赖包是否存在依赖类 Class

方法一&#xff1a;通过 Linux &#xff0c;grep -r ClassPath 命令 grep -f org.apache.kafka.connect.source.SourceRecord在 jar 包所在 lib 或者 lib/plugins 目录下执行&#xff0c;grep -r&#xff0c; flink-sql-connector-sqlserver-cdc-3.3.0.jar 中此 kafka Source…...

41:像素坐标与实际坐标转化

采用上面的算子 将像素坐标点转换为实际坐标 image_points_to_world_plane(CamParam, Worldpose, Row, Column, m, X, Y) 第一个参数&#xff1a;标定得到的内参--根据标定助手得到的 第二个参数&#xff1a;标定得到的外参--根据标定助手得到的 第三个参数&#xff1a;计算…...

大某麦演唱会门票如何自动抢

引言 仅供学习研究&#xff0c;欢迎交流 抢票难&#xff0c;难于上青天&#xff01;无论是演唱会、话剧还是体育赛事&#xff0c;大麦网的票总是秒光。大麦网是国内知名的票务平台&#xff0c;热门演出票往往一票难求。手动抢票不仅耗时&#xff0c;还容易错过机会。作为一名…...

人工智能外呼系统:重构智能交互的全维度进化

在数字化浪潮席卷全球的今天&#xff0c;人工智能外呼系统正以其颠覆性的技术革新&#xff0c;重新定义企业与客户的沟通范式。这一融合语音识别、自然语言处理与机器学习的智能系统&#xff0c;不仅实现了从 “机械应答” 到 “智慧交互” 的跨越&#xff0c;更在金融、医疗、…...

LVS负载均衡群集和keepalive

目录 一. 集群概述 1.1 集群的定义 1.2 集群的分类 1. 高可用集群 HA 2. 高性能运输群集 HPC 3.负载均衡群集 LB 4. 分布式存储集群 二. LVS概述 2.1 LVS的定义 2.2 LVS的工作原理 2.3 LVS 的三种工作模式 2.4 LVS 三种工作模式的对比 2.5 LVS 调度算法 1. 静态…...

在 Excel 中有效筛选重复元素

如果是新版excel UNIQUE(FILTER(D2:D19, COUNTIF(D2:D19, D2:D19)>1)) 旧版 Excel&#xff08;如 2019/2016/2013&#xff09; 使用方法&#xff1a;在 E2 单元格输入此公式。按 Ctrl Shift Enter&#xff08;数组公式&#xff09;。向下拖动填充至空白行为止。 IFERROR…...

Apache Pulsar 消息、流、存储的融合

Apache Pulsar 消息、流、存储的融合 消息队列在大层面有两种不同类型的应用&#xff0c;一种是在线系统的message queue&#xff0c;一种是流计算&#xff0c;data pipeline的streaming高throughout&#xff0c;一致性较低&#xff0c;延迟较差的过程。 存算分离 扩容和缩容快…...

最优化方法Python计算:有约束优化应用——线性可分问题支持向量机

设问题的数据样本点 ( x i , y i ) (\boldsymbol{x}_i,y_i) (xi​,yi​)&#xff0c; x i ∈ R n \boldsymbol{x}_i\in\text{R}^n xi​∈Rn&#xff0c; y i 1 y_i\pm1 yi​1&#xff0c; i 1 , 2 , ⋯ , m i1,2,\cdots,m i1,2,⋯,m。由于标签数据 y i ∈ { − 1 , 1 } y_i\…...

Linux 离线安装 Docker 和 Docker Compose 最新版 的完整指南

一、准备工作 1. 下载安装包​&#xff08;需在有网络的机器操作&#xff09;&#xff1a; Docker 引擎&#xff1a;从官方仓库下载最新二进制包 wget https://download.docker.com/linux/static/stable/x86_64/docker-24.0.6.tgz​Docker Compose&#xff1a;下载最新二进制…...

SpringBoot学习(上) , SpringBoot项目的创建(IDEA2024版本)

目录 1. SpringBoot介绍 SpringBoot特点 2. SpringBoot入门 2.1 创建SpringBoot项目 Spring Initialize 第一步: 选择创建项目 第二步: 选择起步依赖 第三步: 查看启动类 2.2 springboot父项目 2.3 测试案例 2.3.1 数据库 2.3.2 生成代码 1. SpringBoot介绍 Spring B…...

数据结构(四)——栈的应用—数制转换

利用栈进行数制转换&#xff1a; 十进制转换八进制&#xff1a;先将十进制数除以八得到余数&#xff0c;余数入栈&#xff0c;然后将得到的商继续除以八&#xff0c;直到商为零 #include <stdio.h> #include <stdlib.h>#define MAXSIZE 100//数制转换//定义链表节…...

多视角系统,视角之间的切换,输入操作。无人机Pawn视角的实现

一.创建自己的PlayerController。它相当于是灵魂&#xff0c;穿梭在不同Pawn之间。也即是切换视角。不同输入的响应也写在这里。这样即使&#xff0c;都有鼠标操作&#xff0c;也能区分。避免了代码的重复耦合。也可以叫做视角系统。 class LZJGAMEMODE_API ALZJPlayerControl…...

【Redis 进阶】哨兵模式

思维导图&#xff1a; 一、哨兵模式概述 &#xff08;一&#xff09;传统主从复制模式的局限性 在传统的Redis主从复制架构中&#xff0c;若主节点发生故障&#xff0c;运维人员需手动执行故障转移操作&#xff0c;将一个从节点提升为新主节点&#xff0c;并逐一通知所有客户…...

掌控随心 - 服务网格的流量管理艺术 (Istio 实例)

掌控随心 - 服务网格的流量管理艺术 (Istio 实例) 想象一下,没有服务网格的时候,我们要实现像“将 1% 的用户流量导入到新版本应用”、“根据用户设备类型访问不同后端”、“模拟下游服务故障”这类高级流量策略,通常需要在代码、负载均衡器、API 网关等多个地方进行复杂且分…...

在服务器排查java某个线程导致CPU飙高教程

&#x1f9e9; 第一步&#xff1a;确定是哪个线程占用 CPU 高 1.1 使用 top 或 htop 命令查看高 CPU 的 Java 进程 top -Hp <Java进程PID>-H 表示显示线程级别&#xff1b;-p 后面跟 Java 主进程的 PID&#xff1b;你会看到各个线程的 CPU 使用情况&#xff0c;找出使用…...

电子商城后台管理平台-Flask Vue项目开发

电子商城后台管理系统 电子商城后台管理平台-Flask 项目开发 文章目录 前言网课内容数据库参数用户模块/user/前端LESS 的语法使用LESS 的特点 ui登录页面主页页面头部左边aside 菜单main主体后端main页面设置用户-角色-权限商品分类商品信息图片保存到项目里面订单数据展示 网…...

Lettuce 节点刷新、连接优化与 Spring 升级适配全解析:从环境约束到生产验证

引言 在分布式系统中&#xff0c;Redis 作为高性能缓存中间件被广泛使用。随着 Spring 生态的迭代&#xff08;尤其是 Spring Boot 2.0 的普及&#xff09;&#xff0c;Lettuce 逐渐取代 Jedis 成为 Redis 客户端的 “默认选择”。但开发者常面临三个核心问题&#xff1a;Let…...

CVE-2025-31258 macOS远程视图服务沙箱逃逸漏洞PoC已公开

苹果公司近日针对macOS系统中新披露的CVE-2025-31258漏洞发布补丁&#xff0c;该漏洞可能允许恶意应用程序突破沙箱限制&#xff0c;获取未授权的系统资源访问权限。在安全研究员Seo Hyun-gyu公开概念验证&#xff08;PoC&#xff09;利用代码后&#xff0c;该漏洞已在macOS Se…...

UDP协议详细讲解及C++代码实例

目录 一、UDP协议概述二、UDP通信流程1)数据传输2)无连接 三、关键点解析1&#xff09; 套接字创建2&#xff09;无连接特性3&#xff09;数据传输 四、总结 UDP协议详细讲解及C代码实例 一、UDP协议概述 UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&a…...

武汉大学无人机视角下的多目标指代理解新基准!RefDrone:无人机场景指代表达理解数据集

作者&#xff1a;Zhichao Sun, Yepeng Liu, Huachao Zhu, Yuliang Gu, Yuda Zou, Zelong Liu, Gui-Song Xia, Bo Du, Yongchao Xu 单位&#xff1a;武汉大学计算机学院 论文标题&#xff1a;RefDrone: A Challenging Benchmark for Drone Scene Referring Expression Compreh…...

【递归、搜索和回溯】二叉树中的深搜

个人主页 &#xff1a; zxctscl 专栏 【C】、 【C语言】、 【Linux】、 【数据结构】、 【算法】 如有转载请先通知 文章目录 前言1 2331. 计算布尔二叉树的值1.1 分析1.2 代码 2 129. 求根节点到叶节点数字之和2.1 分析2.2 代码 3 814. 二叉树剪枝3.1 分析3.2 代码 4 98. 验证…...

Algolia - Docsearch的申请配置安装【以踩坑解决版】

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…...

Linux513 rsync本地传输 跨设备传输 一

ping节点bPing通 仅主机模式不需要设置网关节点a也可以Ping通节点b 同步成功 下载文件夹成功 今日源码 节点a 节点b...

leetcode 383. Ransom Note

题目描述 代码 class Solution { public:bool canConstruct(string ransomNote, string magazine) {vector<int> table(26,0);for(char ch : magazine){table[ch-a];}for(char ch : ransomNote){table[ch-a]--;if(table[ch-a] < 0)return false;}return true;} };...

Skyvern:用 AI+视觉驱动浏览器自动化

Skyvern&#xff1a;用 AI视觉驱动浏览器自动化 一、前言二、项目概览2.1 Skyvern 项目简介2.2 代码结构与模块划分 三、环境搭建与快速上手3.1 环境准备3.1.1 系统与依赖3.1.2 克隆项目3.1.3 安装 Python 依赖3.1.4 配置环境变量3.1.5 启动服务 3.2 验证安装 四、核心功能与实…...

数据库原理期末考试速成--最后附带两套题

引言 为什么从3开始呢,毕竟是速成吗,总要放弃一些东西 前两章1.概论 2.关系数据库:这里面都是一些运算符什么的,我感觉都学这个:笛卡尔积之列的都会算 这两章比较重要的我就放在这里了 选择、投影、连接、除、并、交、差,其中选择、投影、并、差、笛卡尔积是5种基本关…...

《探索React Native社交应用中WebRTC实现低延迟音视频通话的奥秘》

WebRTC&#xff0c;全称为Web Real-Time Communication&#xff0c;是一项开创性的开源技术&#xff0c;为Web和移动应用开启了实时通信的大门。它打破了传统通信的束缚&#xff0c;使得应用之间无需依赖繁琐的中间服务器&#xff0c;就能实现直接的点对点通信&#xff0c;这是…...