51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

公司项目配置了 AJ-Captcha 验证码,竟然提升用户100%验证率!

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

赞(5)
未经允许不得转载:工具盒子 » 公司项目配置了 AJ-Captcha 验证码,竟然提升用户100%验证率!