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

news2025/7/15 10:29:14

[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 {

    @Bean
    public 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 {
    @Autowired
    private 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 {

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Bean
    public 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();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }

6. 创建验证码过滤器

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


public class CaptchaFilter extends OncePerRequestFilter {

    @Override
    protected 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 {

    @Override
    protected 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 添加验证码。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2375830.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

万文c++继承

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

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;还容易错过机会。作为一名…

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. 静态…

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\…

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…

【Redis 进阶】哨兵模式

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

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

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

武汉大学无人机视角下的多目标指代理解新基准!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种基本关…

数据结构基础--蓝桥杯备考

1.优缺点总述 STL中各容器对比图 各类线性数据结构优缺点 1.数组 1.优点 1.简单&#xff0c;容易理解 2.访问快捷&#xff0c;只需要用下标就可以 3.有某些应用场景直接对应&#xff0c;例如二维数组对应平面 2.缺点 删除和插入数据非常耗时 2.链表 1.优点 插入和删…

2.4GHz无线通信芯片选型指南:集成SOC与低功耗方案解析

今天给大家分享几款2.4GHz无线通信芯片方案&#xff1a; 一、集成SOC芯片方案 XL2407P&#xff08;芯岭技术&#xff09; 集成射频收发机和微控制器&#xff08;如九齐NY8A054E&#xff09; 支持一对多组网和自动重传 发射功率8dBm&#xff0c;接收灵敏度-96.5dBm&#xff08…

Unity_JK框架【5】音效系统实现

在游戏开发中&#xff0c;音频是不可或缺的一部分&#xff0c;它能够极大地增强游戏的沉浸感和趣味性。今天&#xff0c;我们就用JK框架 探讨一下如何在Unity中实现一个强大的音频系统&#xff0c;并且通过实际的测试脚本来验证其功能&#x1f44f;。 一、音频模块类&#xff1…