Java后台手机验证码处理

演示Java后台处理手机验证码的完整流程,包括生成、发送、验证和安全性考虑

验证码测试

验证结果:

验证结果将显示在这里...

Java 核心代码

验证码服务类

import org.springframework.stereotype.Service;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 手机验证码服务
 */
@Service
public class SmsCodeService {

    // 存储验证码,key: 手机号, value: 验证码和过期时间
    private final ConcurrentHashMap<String, CodeInfo> codeCache =
        new ConcurrentHashMap<>();

    // 验证码有效期(分钟)
    private static final int CODE_EXPIRE_MINUTES = 5;

    // 验证码长度
    private static final int CODE_LENGTH = 6;

    // 发送间隔(秒)
    private static final int SEND_INTERVAL_SECONDS = 60;

    /**
     * 生成验证码
     */
    public String generateCode() {
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < CODE_LENGTH; i++) {
            sb.append(random.nextInt(10));
        }
        return sb.toString();
    }

    /**
     * 发送验证码
     */
    public boolean sendCode(String phone) {
        // 检查发送频率
        if (isTooFrequent(phone)) {
            return false;
        }

        // 生成验证码
        String code = generateCode();

        // 存储验证码信息
        CodeInfo codeInfo = new CodeInfo(code,
            System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(CODE_EXPIRE_MINUTES));
        codeCache.put(phone, codeInfo);

        // 记录发送时间
        recordSendTime(phone);

        // 调用短信服务发送验证码(这里模拟发送)
        return sendSms(phone, code);
    }

    /**
     * 验证验证码
     */
    public boolean verifyCode(String phone, String inputCode) {
        CodeInfo codeInfo = codeCache.get(phone);
        if (codeInfo == null) {
            return false; // 验证码不存在
        }

        // 检查是否过期
        if (System.currentTimeMillis() > codeInfo.getExpireTime()) {
            codeCache.remove(phone); // 移除过期验证码
            return false;
        }

        // 验证码是否正确
        if (codeInfo.getCode().equals(inputCode)) {
            codeCache.remove(phone); // 验证成功后移除验证码
            return true;
        }

        return false;
    }

    /**
     * 检查发送频率
     */
    private boolean isTooFrequent(String phone) {
        // 实现发送频率检查逻辑
        // 这里简化实现,实际应该存储发送时间并检查
        return false;
    }

    /**
     * 记录发送时间
     */
    private void recordSendTime(String phone) {
        // 记录发送时间,用于频率控制
    }

    /**
     * 发送短信(模拟实现)
     */
    private boolean sendSms(String phone, String code) {
        // 这里模拟发送短信
        System.out.println("发送短信到 " + phone + ": 验证码是 " + code);

        // 实际项目中这里会调用短信服务商的API
        // 例如阿里云短信服务、腾讯云短信服务等

        return true;
    }

    /**
     * 验证码信息类
     */
    private static class CodeInfo {
        private final String code;
        private final long expireTime;

        public CodeInfo(String code, long expireTime) {
            this.code = code;
            this.expireTime = expireTime;
        }

        public String getCode() {
            return code;
        }

        public long getExpireTime() {
            return expireTime;
        }
    }
}
1

验证码生成与发送

Java后台生成6位数字验证码,并通过短信服务发送给用户。

// 生成6位数字验证码
public String generateCode() {
    Random random = new Random();
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 6; i++) {
        sb.append(random.nextInt(10));
    }
    return sb.toString();
}

// 发送验证码
public boolean sendCode(String phone) {
    String code = generateCode();

    // 存储验证码(设置5分钟过期)
    codeCache.put(phone, new CodeInfo(code,
        System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5)));

    // 调用短信服务发送
    return sendSms(phone, code);
}
2

验证码验证

用户提交验证码后,后台进行验证,包括检查是否存在、是否过期、是否正确。

// 验证验证码
public boolean verifyCode(String phone, String inputCode) {
    CodeInfo codeInfo = codeCache.get(phone);

    // 检查验证码是否存在
    if (codeInfo == null) {
        return false;
    }

    // 检查是否过期
    if (System.currentTimeMillis() > codeInfo.getExpireTime()) {
        codeCache.remove(phone);
        return false;
    }

    // 检查验证码是否正确
    if (codeInfo.getCode().equals(inputCode)) {
        codeCache.remove(phone); // 验证成功后移除
        return true;
    }

    return false;
}
用户请求发送验证码
后端生成验证码并存储
调用短信服务发送验证码
用户输入验证码提交
后端验证验证码
返回验证结果

Java验证码处理详解

存储方案选择

  • 内存存储:简单快速,适合单机应用
  • Redis:分布式支持,自动过期,性能优秀
  • 数据库:持久化存储,适合重要业务场景
  • Ehcache:本地缓存,性能较好

安全性考虑

  • 频率限制:防止恶意发送验证码
  • IP限制:限制同一IP的发送次数
  • 验证码复杂度:适当长度的数字或字母数字组合
  • 时效性:设置合理的过期时间

最佳实践

  • 使用线程安全的ConcurrentHashMap或Redis存储验证码
  • 设置合理的验证码有效期(通常5-10分钟)
  • 实现发送频率限制,防止短信轰炸
  • 验证成功后立即删除验证码,防止重复使用
  • 记录发送日志,便于排查问题
  • 使用专业的短信服务商,确保送达率

注意事项

  • 不要在前端生成验证码,必须在后端生成
  • 验证码不能过于简单,避免被暴力破解
  • 生产环境不要使用System.out打印验证码
  • 考虑短信服务商调用失败的重试机制
  • 注意验证码存储的内存泄漏问题

完整Controller示例

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/sms")
public class SmsController {

    @Autowired
    private SmsCodeService smsCodeService;

    /**
     * 发送验证码
     */
    @PostMapping("/send")
    public ApiResult<String> sendCode(@RequestBody SendCodeRequest request) {
        // 验证手机号格式
        if (!isValidPhone(request.getPhone())) {
            return ApiResult.error("手机号格式不正确");
        }

        // 发送验证码
        boolean success = smsCodeService.sendCode(request.getPhone());

        if (success) {
            return ApiResult.success("验证码发送成功");
        } else {
            return ApiResult.error("验证码发送失败,请稍后重试");
        }
    }

    /**
     * 验证验证码
     */
    @PostMapping("/verify")
    public ApiResult<String> verifyCode(@RequestBody VerifyCodeRequest request) {
        boolean isValid = smsCodeService.verifyCode(request.getPhone(), request.getCode());

        if (isValid) {
            return ApiResult.success("验证码正确");
        } else {
            return ApiResult.error("验证码错误或已过期");
        }
    }

    /**
     * 验证手机号格式
     */
    private boolean isValidPhone(String phone) {
        // 简单的手机号格式验证
        return phone != null && phone.matches("^1[3-9]\\d{9}$");
    }

    /**
     * 发送验证码请求参数
     */
    public static class SendCodeRequest {
        private String phone;

        // getter和setter
        public String getPhone() { return phone; }
        public void setPhone(String phone) { this.phone = phone; }
    }

    /**
     * 验证验证码请求参数
     */
    public static class VerifyCodeRequest {
        private String phone;
        private String code;

        // getter和setter
        public String getPhone() { return phone; }
        public void setPhone(String phone) { this.phone = phone; }
        public String getCode() { return code; }
        public void setCode(String code) { this.code = code; }
    }
}