公司项目配置了 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 验证码,并实现了用户名、密码和验证码的登录验证功能,显著提升了用户验证率,为公司项目的安全性和用户体验提供了有力保障。