公司项目配置了 AJ-Captcha 验证码,竟然提升用户100%验证率!
在当今数字化的时代,用户验证对于保障公司项目的安全性和可靠性至关重要。为了提升用户验证的效率和准确性,我们在项目中引入了 AJ-Captcha 验证码,并取得了令人瞩目的成果------用户验证率竟然提升了 100%!接下来,让我们深入探讨这一神奇转变背后的技术实现。
AJ-Captcha 行为验证码包含滑动拼图、文字点选两种方式,UI 支持弹出和嵌入两种方式。后端提供 Java 实现,前端提供了 php、angular、html、vue、uni-app、flutter、android、ios 等代码示例。
为了实现高效的数据管理,我们在项目中采用了 MyBatis-Plus 作为数据库持久框架。MyBatis-Plus 简化了数据库操作,提供了强大的 CRUD 功能和便捷的分页查询等特性,大大提高了开发效率。
运行效果:
若想获取项目完整代码以及其他文章的项目源码,且在代码编写时遇到问题需要咨询交流,欢迎加入下方的知识星球。
项目配置
首先,让我们看看项目的 pom.xml 配置。以下是添加 Anji-plus 验证码和 MyBatis-Plus 相关依赖的部分
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.3.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.icoderoad</groupId> <artifactId>AJ-Captcha</artifactId> <version>0.0.1-SNAPSHOT</version> <name>AJ-Captcha</name> <description>AJ-Captcha Demo project for Spring Boot</description>
<properties> <java.version>17</java.version> <anji-plus.version>1.3.0</anji-plus.version> <mybatis-spring.version>3.0.3</mybatis-spring.version> <mybatis-plus-boot-starter.version>3.5.7</mybatis-plus-boot-starter.version> </properties> <dependencies> <dependency>
<groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<!-- 数据库驱动依赖 -->
<dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency>
<!-- Anji-plus 验证码依赖 --> <dependency>
<groupId>com.anji-plus</groupId> <artifactId>captcha</artifactId> <version>${anji-plus.version}</version> </dependency> <!-- MyBatis-Plus 依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus-boot-starter.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>${mybatis-spring.version}</version> </dependency> <!-- Redis 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- 其他依赖 --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</project>
在 yaml 属性文件中,我们进行了相关的配置:
server:
port: 8080
# 数据库配置
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/captcha
username: root
password: root
redis:
host: localhost
password: 123456
port: 6379
# Anji-plus 验证码配置
aj:
captcha:
# 缓存类型
cache-type: redis
# blockPuzzle 滑块 clickWord 文字点选 default默认两者都实例化
type: blockPuzzle
# 校验滑动拼图允许误差偏移量(默认5像素)
slip-offset: 5
# aes加密坐标开启或者禁用(true|false)
aes-status: true
# 滑动干扰项(0/1/2)
interference-options: 0
# 右下角水印
water-mark: "路条编程"
数据库表设计
下面是用户表的 DDL 语句定义:
CREATE TABLE `users` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `username` VARCHAR(50) NOT NULL, `password` VARCHAR(255) NOT NULL );
INSERT INTO
users
(username
,password
) VALUES ('admin', '$2a$10$RjHjpNXCK641apxBPd124ObuUr/MblGf4Sbegrpm8Wks/0yh7I2ve'); -- 这里假设使用 BCrypt 算法对 'admin123' 进行加密后的结果
MyBatis-Plus 相关代码实现
package com.icoderoad.AJ_Captcha.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data @TableName("users") public class User { private Long id; private String username; private String password; }
package com.icoderoad.AJ_Captcha.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.icoderoad.AJ_Captcha.entity.User;
public interface UserMapper extends BaseMapper<User> { }
package com.icoderoad.AJ_Captcha.service;
import org.springframework.stereotype.Service;
import com.icoderoad.AJ_Captcha.entity.User;
@Service public interface UserService { User getUserByUsername(String username); }
package com.icoderoad.AJ_Captcha.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.icoderoad.AJ_Captcha.entity.User; import com.icoderoad.AJ_Captcha.mapper.UserMapper; import com.icoderoad.AJ_Captcha.service.UserService;
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override public User getUserByUsername(String username) { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("username", username); return this.getOne(queryWrapper); } }
后端代码实现
package com.icoderoad.AJ_Captcha.controller;
import java.util.HashMap; import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping;
import com.icoderoad.AJ_Captcha.entity.User; import com.icoderoad.AJ_Captcha.service.UserService;
import jakarta.servlet.http.HttpServletRequest;
@Controller public class UserController {
@Autowired private UserService userService;
@GetMapping("/") public String index(Model model) { return "index"; }
@PostMapping("/login") public ResponseEntity<Map<String, Boolean>> login(HttpServletRequest request) { String username = request.getParameter("username"); String password = request.getParameter("password");
User user = userService.getUserByUsername(username); boolean isAuthenticated = false; BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); System.out.println(passwordEncoder.encode(password));; if (user!= null && passwordEncoder.matches(password, user.getPassword())) { isAuthenticated = true; }
Map<String, Boolean> response = new HashMap<>(); response.put("success", isAuthenticated);
return new ResponseEntity<>(response, HttpStatus.OK); } }
前端页面实现
在前端,我们使用 Thymeleaf 模板结合 JavaScript 和 Bootstrap 来展示和验证验证码,并实现用户名、密码和验证码的登录验证功能。以下是相关代码片段:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>验证码示例</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> <link rel="stylesheet" type="text/css" href="/css/verify.css"> <script src="/js/crypto-js.js"></script> <script src="/js/ase.js"></script> <script src="/js/verify.js"></script> <script> (function () { if (!window.Promise) { document.writeln('<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-promise/4.1.1/es6-promise.min.js"><' + '/' +'script>'); } })(); </script> <style> body { display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100vh; }
.container { margin-top: -80px; width: 78%; display: flex; flex-direction: column; justify-content: center; align-items: center; border-radius: 10px; border: 1px solid gray; padding: 25px; } form { width: 80%; }
.btn { border: none; outline: none; width: 300px; height: 40px; line-height: 40px; text-align: center; cursor: pointer; background-color: #409EFF; color: #fff; font-size: 16px; letter-spacing: 1em; } </style> </head> <body>
<div class="container"> <h2>请登录</h2> <form th:action="@{/login}" method="post"> <div class="form-group"> <label for="username">用户名</label> <input type="text" class="form-control" id="username" name="username" /> </div> <div class="form-group"> <label for="password">密码</label> <input type="password" class="form-control" id="password" name="password" /> </div> <div class="form-group"> <button class="btn btn-primary" id='btn' type="button" >登录</button> <div id="mpanel" style="margin-top:50px;"></div> <div id="tip" style="margin-top:50px;"></div> </div> </form> </div> <script> $('#mpanel').slideVerify({ baseUrl: 'http://localhost:8080', mode: 'pop', containerId: 'btn',//pop模式 必填 被点击之后出现行为验证码的元素id imgSize: { width: '400px', height: '200px' }, barSize: { width: '400px', height: '40px' }, beforeCheck: function () { var name = $("#username").val(); var pass = $('#password').val(); if (name === '' || pass === '') { $("#tip").html('<div class="alert alert-danger">请输入用户名和密码!</div>'); setTimeout(function() { $("#tip div.alert").fadeOut(500); }, 5000); return false; } return true; }, ready: function () {}, success: function (params) { var name = $("#username").val(); var pass = $('#password').val(); $.ajax({ type: "POST", url: "/login", data: { username: name, password: pass }, success: function (response) { if (response.success) { $("#tip").html('<div class="alert alert-success">登录成功</div>'); } else { $("#tip").html('<div class="alert alert-danger">登录失败</div>'); } setTimeout(function() { $("#tip div.alert").fadeOut(500); }, 5000); }, error: function () { $("#tip").html('<div class="alert alert-danger">登录请求出错</div>'); setTimeout(function() { $("#tip div.alert").fadeOut(500); }, 5000); } }); }, error: function () {} }); </script>
</body> </html>
通过以上前后端的协同工作,我们成功地配置了 AJ-Captcha 验证码,并实现了用户名、密码和验证码的登录验证功能,显著提升了用户验证率,为公司项目的安全性和用户体验提供了有力保障。