最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot中登錄驗證碼的4種實現(xiàn)方案

 更新時間:2025年04月29日 08:27:15   作者:風(fēng)象南  
這篇文章主要介紹了在SpringBoot應(yīng)用中實現(xiàn)四種登錄驗證碼的技術(shù)方案,包括圖形驗證碼、短信驗證碼、郵箱驗證碼和滑動拼圖驗證碼,需要的可以參考下

在當(dāng)今互聯(lián)網(wǎng)安全形勢日益嚴(yán)峻的環(huán)境下,驗證碼已成為保護用戶賬戶安全、防止破解和自動化攻擊的重要手段。尤其在登錄系統(tǒng)中,合理使用驗證碼不僅能有效阻止機器人批量嘗試賬號密碼,還能降低賬戶被盜風(fēng)險,提升系統(tǒng)安全性。

本文將詳細(xì)介紹在SpringBoot應(yīng)用中實現(xiàn)四種登錄驗證碼的技術(shù)方案,包括圖形驗證碼、短信驗證碼、郵箱驗證碼和滑動拼圖驗證碼。

方案一:基于Kaptcha的圖形驗證碼

原理介紹

圖形驗證碼是最傳統(tǒng)且應(yīng)用最廣泛的驗證碼類型,原理是在服務(wù)端生成隨機字符串并渲染成圖片,用戶需要識別圖片中的字符并輸入。圖形驗證碼實現(xiàn)簡單,對用戶體驗影響較小,是中小型應(yīng)用的理想選擇。

實現(xiàn)步驟

1. 添加Kaptcha依賴

<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
</dependency>

2. 配置Kaptcha生成器

@Configuration
public class KaptchaConfig {
    
    @Bean
    public Producer kaptchaProducer() {
        Properties properties = new Properties();
        // 圖片寬度
        properties.setProperty(Constants.KAPTCHA_IMAGE_WIDTH, "150");
        // 圖片高度
        properties.setProperty(Constants.KAPTCHA_IMAGE_HEIGHT, "50");
        // 字體大小
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE, "32");
        // 字體顏色
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
        // 文本集合,驗證碼值從此集合中獲取
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_STRING, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        // 驗證碼長度
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
        // 干擾線顏色
        properties.setProperty(Constants.KAPTCHA_NOISE_COLOR, "blue");
        // 去除背景漸變
        properties.setProperty(Constants.KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
        
        Config config = new Config(properties);
        return config.getProducerImpl();
    }
}

3. 創(chuàng)建驗證碼存儲服務(wù)

@Service
public class CaptchaService {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 驗證碼有效期(秒)
    private static final long CAPTCHA_EXPIRATION = 300;
    
    // 存儲驗證碼
    public void storeCaptcha(String sessionId, String captchaCode) {
        redisTemplate.opsForValue().set(
                "CAPTCHA:" + sessionId, 
                captchaCode, 
                CAPTCHA_EXPIRATION, 
                TimeUnit.SECONDS);
    }
    
    // 驗證驗證碼
    public boolean validateCaptcha(String sessionId, String userInputCaptcha) {
        String key = "CAPTCHA:" + sessionId;
        String storedCaptcha = redisTemplate.opsForValue().get(key);
        
        if (storedCaptcha != null && storedCaptcha.equalsIgnoreCase(userInputCaptcha)) {
            // 驗證成功后立即刪除,防止重復(fù)使用
            redisTemplate.delete(key);
            return true;
        }
        
        return false;
    }
}

4. 實現(xiàn)驗證碼生成控制器

@RestController
@RequestMapping("/captcha")
public class CaptchaController {
    
    @Autowired
    private Producer kaptchaProducer;
    
    @Autowired
    private CaptchaService captchaService;
    
    @GetMapping("/image")
    public void getImageCaptcha(HttpServletResponse response, HttpServletRequest request) throws 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");
        
        // 創(chuàng)建驗證碼文本
        String capText = kaptchaProducer.createText();
        
        // 獲取會話ID
        String sessionId = request.getSession().getId();
        
        // 存儲驗證碼
        captchaService.storeCaptcha(sessionId, capText);
        
        // 創(chuàng)建驗證碼圖片
        BufferedImage image = kaptchaProducer.createImage(capText);
        ServletOutputStream out = response.getOutputStream();
        
        // 輸出圖片
        ImageIO.write(image, "jpg", out);
        out.flush();
        out.close();
    }
}

5. 登錄控制器集成驗證碼校驗

@RestController
@RequestMapping("/auth")
public class AuthController {
    
    @Autowired
    private CaptchaService captchaService;
    
    @Autowired
    private UserService userService;
    
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest, HttpServletRequest request) {
        // 獲取會話ID
        String sessionId = request.getSession().getId();
        
        // 驗證驗證碼
        if (!captchaService.validateCaptcha(sessionId, loginRequest.getCaptcha())) {
            return ResponseEntity.badRequest().body(new ApiResponse(false, "驗證碼錯誤或已過期"));
        }
        
        // 用戶名密碼驗證
        boolean authenticated = userService.authenticate(
                loginRequest.getUsername(), 
                loginRequest.getPassword());
        
        if (authenticated) {
            // 生成Token或設(shè)置會話
            String token = jwtTokenProvider.generateToken(loginRequest.getUsername());
            return ResponseEntity.ok(new JwtAuthResponse(token));
        } else {
            return ResponseEntity.badRequest().body(new ApiResponse(false, "用戶名或密碼錯誤"));
        }
    }
}

6. 前端集成示例

<div class="login-form">
    <form id="loginForm">
        <div class="form-group">
            <label for="username">用戶名</label>
            <input type="text" class="form-control" id="username" name="username" required>
        </div>
        <div class="form-group">
            <label for="password">密碼</label>
            <input type="password" class="form-control" id="password" name="password" required>
        </div>
        <div class="form-group">
            <label for="captcha">驗證碼</label>
            <div class="captcha-container">
                <input type="text" class="form-control" id="captcha" name="captcha" required>
                <img id="captchaImg" src="/captcha/image" alt="驗證碼" onclick="refreshCaptcha()">
            </div>
        </div>
        <button type="submit" class="btn btn-primary">登錄</button>
    </form>
</div>
<script>
function refreshCaptcha() {
    document.getElementById('captchaImg').src = '/captcha/image?t=' + new Date().getTime();
}

document.getElementById('loginForm').addEventListener('submit', function(e) {
    e.preventDefault();
    
    const data = {
        username: document.getElementById('username').value,
        password: document.getElementById('password').value,
        captcha: document.getElementById('captcha').value
    };
    
    fetch('/auth/login', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(data)
    })
    .then(response => response.json())
    .then(data => {
        if (data.token) {
            localStorage.setItem('token', data.token);
            window.location.href = '/dashboard';
        } else {
            alert(data.message);
            refreshCaptcha();
        }
    })
    .catch(error => {
        console.error('Error:', error);
        refreshCaptcha();
    });
});
</script>

優(yōu)缺點分析

優(yōu)點

  • 實現(xiàn)簡單,開發(fā)成本低
  • 對服務(wù)器資源占用少
  • 用戶體驗相對友好
  • 無需第三方服務(wù),降低依賴

缺點

  • 安全性較低,容易被OCR識別破解
  • 對視障用戶不友好
  • 在移動設(shè)備上體驗不佳
  • 隨著AI發(fā)展,圖形驗證碼的安全性逐漸降低

方案二:基于短信驗證碼

原理介紹

短信驗證碼通過向用戶手機發(fā)送一次性驗證碼實現(xiàn)身份驗證。用戶需要輸入收到的驗證碼完成登錄過程。這種方式不僅驗證了賬號密碼的正確性,還確認(rèn)了用戶對手機號的控制權(quán),大幅提高了安全性。

實現(xiàn)步驟

1. 添加阿里云SDK依賴

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-core</artifactId>
    <version>4.5.3</version>
</dependency>

2. 配置短信服務(wù)參數(shù)

@Configuration
@ConfigurationProperties(prefix = "aliyun.sms")
@Data
public class SmsProperties {
    private String accessKeyId;
    private String accessKeySecret;
    private String signName;
    private String templateCode;
    private String endpoint = "dysmsapi.aliyuncs.com";
}
# application.properties
aliyun.sms.access-key-id=YOUR_ACCESS_KEY_ID
aliyun.sms.access-key-secret=YOUR_ACCESS_KEY_SECRET
aliyun.sms.sign-name=YOUR_SMS_SIGN_NAME
aliyun.sms.template-code=SMS_TEMPLATE_CODE

3. 實現(xiàn)短信服務(wù)

@Service
@Slf4j
public class SmsService {
    
    @Autowired
    private SmsProperties smsProperties;
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    private static final long SMS_EXPIRATION = 300; // 5分鐘過期
    private static final String SMS_PREFIX = "SMS_CAPTCHA:";
    
    /**
     * 發(fā)送短信驗證碼
     * @param phoneNumber 手機號
     * @return 是否發(fā)送成功
     */
    public boolean sendSmsCaptcha(String phoneNumber) {
        try {
            // 生成6位隨機驗證碼
            String captcha = generateCaptcha(6);
            
            // 存儲驗證碼
            redisTemplate.opsForValue().set(
                    SMS_PREFIX + phoneNumber, 
                    captcha, 
                    SMS_EXPIRATION, 
                    TimeUnit.SECONDS);
            
            // 構(gòu)建短信客戶端
            DefaultProfile profile = DefaultProfile.getProfile(
                    "cn-hangzhou", 
                    smsProperties.getAccessKeyId(), 
                    smsProperties.getAccessKeySecret());
            IAcsClient client = new DefaultAcsClient(profile);
            
            // 構(gòu)建短信請求
            CommonRequest request = new CommonRequest();
            request.setSysMethod(MethodType.POST);
            request.setSysDomain(smsProperties.getEndpoint());
            request.setSysVersion("2017-05-25");
            request.setSysAction("SendSms");
            request.putQueryParameter("RegionId", "cn-hangzhou");
            request.putQueryParameter("PhoneNumbers", phoneNumber);
            request.putQueryParameter("SignName", smsProperties.getSignName());
            request.putQueryParameter("TemplateCode", smsProperties.getTemplateCode());
            
            // 設(shè)置模板參數(shù),將驗證碼作為參數(shù)傳入
            Map<String, String> templateParam = Map.of("code", captcha);
            request.putQueryParameter("TemplateParam", new ObjectMapper().writeValueAsString(templateParam));
            
            // 發(fā)送短信
            CommonResponse response = client.getCommonResponse(request);
            log.info("短信發(fā)送結(jié)果: {}", response.getData());
            
            // 解析響應(yīng)
            JsonNode responseJson = new ObjectMapper().readTree(response.getData());
            return "OK".equals(responseJson.get("Code").asText());
            
        } catch (Exception e) {
            log.error("發(fā)送短信驗證碼失敗", e);
            return false;
        }
    }
    
    /**
     * 驗證短信驗證碼
     * @param phoneNumber 手機號
     * @param captcha 用戶輸入的驗證碼
     * @return 是否驗證成功
     */
    public boolean validateSmsCaptcha(String phoneNumber, String captcha) {
        String key = SMS_PREFIX + phoneNumber;
        String storedCaptcha = redisTemplate.opsForValue().get(key);
        
        if (storedCaptcha != null && storedCaptcha.equals(captcha)) {
            // 驗證成功后刪除驗證碼,防止重復(fù)使用
            redisTemplate.delete(key);
            return true;
        }
        
        return false;
    }
    
    /**
     * 生成指定長度的隨機數(shù)字驗證碼
     */
    private String generateCaptcha(int length) {
        StringBuilder captcha = new StringBuilder();
        Random random = new Random();
        
        for (int i = 0; i < length; i++) {
            captcha.append(random.nextInt(10));
        }
        
        return captcha.toString();
    }
}

4. 實現(xiàn)短信驗證碼控制器

@RestController
@RequestMapping("/captcha")
public class SmsCaptchaController {
    
    @Autowired
    private SmsService smsService;
    
    @PostMapping("/sms/send")
    public ResponseEntity<?> sendSmsCaptcha(@RequestBody PhoneNumberRequest request) {
        String phoneNumber = request.getPhoneNumber();
        
        // 驗證手機號格式
        if (!isValidPhoneNumber(phoneNumber)) {
            return ResponseEntity.badRequest().body(new ApiResponse(false, "手機號格式不正確"));
        }
        
        // 發(fā)送短信驗證碼
        boolean sent = smsService.sendSmsCaptcha(phoneNumber);
        
        if (sent) {
            return ResponseEntity.ok(new ApiResponse(true, "驗證碼已發(fā)送,請注意查收"));
        } else {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse(false, "驗證碼發(fā)送失敗,請稍后再試"));
        }
    }
    
    // 驗證手機號格式(簡化版)
    private boolean isValidPhoneNumber(String phoneNumber) {
        return phoneNumber != null && phoneNumber.matches("^1[3-9]\d{9}$");
    }
}

5. 登錄控制器集成短信驗證碼

@RestController
@RequestMapping("/auth")
public class SmsLoginController {
    
    @Autowired
    private SmsService smsService;
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private JwtTokenProvider jwtTokenProvider;
    
    @PostMapping("/sms-login")
    public ResponseEntity<?> smsLogin(@RequestBody SmsLoginRequest request) {
        String phoneNumber = request.getPhoneNumber();
        String captcha = request.getCaptcha();
        
        // 驗證短信驗證碼
        if (!smsService.validateSmsCaptcha(phoneNumber, captcha)) {
            return ResponseEntity.badRequest().body(new ApiResponse(false, "驗證碼錯誤或已過期"));
        }
        
        // 查找或創(chuàng)建用戶
        User user = userService.findOrCreateByPhone(phoneNumber);
        
        if (user != null) {
            // 生成JWT令牌
            String token = jwtTokenProvider.generateToken(user.getUsername());
            return ResponseEntity.ok(new JwtAuthResponse(token));
        } else {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse(false, "登錄失敗,請稍后再試"));
        }
    }
}

6. 前端集成示例

<div class="sms-login-form">
    <form id="smsLoginForm">
        <div class="form-group">
            <label for="phoneNumber">手機號</label>
            <input type="text" class="form-control" id="phoneNumber" name="phoneNumber" required>
        </div>
        <div class="form-group">
            <label for="smsCaptcha">驗證碼</label>
            <div class="captcha-container">
                <input type="text" class="form-control" id="smsCaptcha" name="smsCaptcha" required>
                <button type="button" class="btn btn-outline-primary" id="sendSmsBtn">獲取驗證碼</button>
            </div>
        </div>
        <button type="submit" class="btn btn-primary">登錄</button>
    </form>
</div>
<script>
let countdown = 60;

document.getElementById('sendSmsBtn').addEventListener('click', function() {
    const phoneNumber = document.getElementById('phoneNumber').value;
    
    if (!phoneNumber || !/^1[3-9]\d{9}$/.test(phoneNumber)) {
        alert('請輸入正確的手機號');
        return;
    }
    
    // 發(fā)送短信驗證碼請求
    fetch('/captcha/sms/send', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ phoneNumber: phoneNumber })
    })
    .then(response => response.json())
    .then(data => {
        alert(data.message);
        if (data.success) {
            startCountdown();
        }
    })
    .catch(error => {
        console.error('Error:', error);
        alert('發(fā)送驗證碼失敗,請稍后再試');
    });
});

function startCountdown() {
    const btn = document.getElementById('sendSmsBtn');
    btn.disabled = true;
    
    let timer = setInterval(() => {
        btn.textContent = `${countdown}秒后重新獲取`;
        countdown--;
        
        if (countdown < 0) {
            clearInterval(timer);
            btn.disabled = false;
            btn.textContent = '獲取驗證碼';
            countdown = 60;
        }
    }, 1000);
}

document.getElementById('smsLoginForm').addEventListener('submit', function(e) {
    e.preventDefault();
    
    const data = {
        phoneNumber: document.getElementById('phoneNumber').value,
        captcha: document.getElementById('smsCaptcha').value
    };
    
    fetch('/auth/sms-login', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(data)
    })
    .then(response => response.json())
    .then(data => {
        if (data.token) {
            localStorage.setItem('token', data.token);
            window.location.href = '/dashboard';
        } else {
            alert(data.message);
        }
    })
    .catch(error => {
        console.error('Error:', error);
        alert('登錄失敗,請稍后再試');
    });
});
</script>

優(yōu)缺點分析

優(yōu)點

  • 安全性高,驗證了用戶對手機的控制權(quán)
  • 可以作為無密碼登錄的方式,簡化用戶操作
  • 適合移動應(yīng)用和需要高安全性的場景
  • 可與其他驗證方式結(jié)合,實現(xiàn)多因素認(rèn)證

缺點

  • 依賴第三方短信服務(wù),存在成本
  • 可能受網(wǎng)絡(luò)和運營商影響,導(dǎo)致延遲
  • 對國際用戶不友好
  • 用戶頻繁登錄時,體驗不佳

方案三:基于Spring Mail的郵箱驗證碼

原理介紹

郵箱驗證碼通過向用戶注冊的電子郵箱發(fā)送一次性驗證碼實現(xiàn)身份驗證。這種方式與短信驗證碼類似,但使用電子郵件作為傳遞媒介,適合對成本敏感或不需要實時驗證的場景。

實現(xiàn)步驟

1. 添加Spring Mail依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

2. 配置郵件服務(wù)

# application.properties
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=your-email@gmail.com
spring.mail.password=your-app-password
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true

# 自定義配置
app.email.from=noreply@yourapp.com
app.email.personal=Your App Name

3. 創(chuàng)建郵件驗證碼服務(wù)

@Service
@Slf4j
public class EmailCaptchaService {
    
    @Autowired
    private JavaMailSender mailSender;
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    @Autowired
    private TemplateEngine templateEngine;
    
    @Value("${app.email.from}")
    private String fromEmail;
    
    @Value("${app.email.personal}")
    private String emailPersonal;
    
    private static final long EMAIL_CAPTCHA_EXPIRATION = 600; // 10分鐘過期
    private static final String EMAIL_CAPTCHA_PREFIX = "EMAIL_CAPTCHA:";
    
    /**
     * 發(fā)送郵箱驗證碼
     * @param email 電子郵箱
     * @return 是否發(fā)送成功
     */
    public boolean sendEmailCaptcha(String email) {
        try {
            // 生成6位隨機驗證碼
            String captcha = generateCaptcha(6);
            
            // 存儲驗證碼
            redisTemplate.opsForValue().set(
                    EMAIL_CAPTCHA_PREFIX + email, 
                    captcha, 
                    EMAIL_CAPTCHA_EXPIRATION, 
                    TimeUnit.SECONDS);
            
            // 準(zhǔn)備郵件內(nèi)容
            Context context = new Context();
            context.setVariable("captcha", captcha);
            context.setVariable("expirationMinutes", EMAIL_CAPTCHA_EXPIRATION / 60);
            String emailContent = templateEngine.process("email/captcha-template", context);
            
            // 創(chuàng)建MIME郵件
            MimeMessage message = mailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
            
            // 設(shè)置發(fā)件人、收件人、主題和內(nèi)容
            helper.setFrom(new InternetAddress(fromEmail, emailPersonal));
            helper.setTo(email);
            helper.setSubject("登錄驗證碼");
            helper.setText(emailContent, true);
            
            // 發(fā)送郵件
            mailSender.send(message);
            log.info("郵箱驗證碼已發(fā)送至: {}", email);
            
            return true;
        } catch (Exception e) {
            log.error("發(fā)送郵箱驗證碼失敗", e);
            return false;
        }
    }
    
    /**
     * 驗證郵箱驗證碼
     * @param email 電子郵箱
     * @param captcha 用戶輸入的驗證碼
     * @return 是否驗證成功
     */
    public boolean validateEmailCaptcha(String email, String captcha) {
        String key = EMAIL_CAPTCHA_PREFIX + email;
        String storedCaptcha = redisTemplate.opsForValue().get(key);
        
        if (storedCaptcha != null && storedCaptcha.equals(captcha)) {
            // 驗證成功后刪除驗證碼,防止重復(fù)使用
            redisTemplate.delete(key);
            return true;
        }
        
        return false;
    }
    
    /**
     * 生成指定長度的隨機數(shù)字驗證碼
     */
    private String generateCaptcha(int length) {
        StringBuilder captcha = new StringBuilder();
        Random random = new Random();
        
        for (int i = 0; i < length; i++) {
            captcha.append(random.nextInt(10));
        }
        
        return captcha.toString();
    }
}

4. 創(chuàng)建郵件模板

<!-- src/main/resources/templates/email/captcha-template.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登錄驗證碼</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            line-height: 1.6;
            color: #333;
        }
        .container {
            max-width: 600px;
            margin: 0 auto;
            padding: 20px;
            border: 1px solid #ddd;
            border-radius: 5px;
        }
        .header {
            text-align: center;
            padding-bottom: 10px;
            border-bottom: 1px solid #eee;
        }
        .content {
            padding: 20px 0;
        }
        .code {
            font-size: 24px;
            font-weight: bold;
            text-align: center;
            padding: 10px;
            margin: 20px 0;
            background-color: #f5f5f5;
            border-radius: 5px;
            letter-spacing: 5px;
        }
        .footer {
            font-size: 12px;
            color: #777;
            text-align: center;
            padding-top: 10px;
            border-top: 1px solid #eee;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h2>登錄驗證碼</h2>
        </div>
        <div class="content">
            <p>您好,</p>
            <p>您正在進行登錄操作,請使用以下驗證碼完成驗證:</p>
            <div class="code" th:text="${captcha}">123456</div>
            <p>驗證碼有效期為 <span th:text="${expirationMinutes}">10</span> 分鐘,請及時使用。</p>
            <p>如果這不是您的操作,請忽略此郵件。</p>
        </div>
        <div class="footer">
            <p>此郵件由系統(tǒng)自動發(fā)送,請勿回復(fù)。</p>
        </div>
    </div>
</body>
</html>

5. 實現(xiàn)郵箱驗證碼控制器

@RestController
@RequestMapping("/captcha")
public class EmailCaptchaController {
    
    @Autowired
    private EmailCaptchaService emailCaptchaService;
    
    @PostMapping("/email/send")
    public ResponseEntity<?> sendEmailCaptcha(@RequestBody EmailRequest request) {
        String email = request.getEmail();
        
        // 驗證郵箱格式
        if (!isValidEmail(email)) {
            return ResponseEntity.badRequest().body(new ApiResponse(false, "郵箱格式不正確"));
        }
        
        // 發(fā)送郵箱驗證碼
        boolean sent = emailCaptchaService.sendEmailCaptcha(email);
        
        if (sent) {
            return ResponseEntity.ok(new ApiResponse(true, "驗證碼已發(fā)送,請查收郵件"));
        } else {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse(false, "驗證碼發(fā)送失敗,請稍后再試"));
        }
    }
    
    // 驗證郵箱格式
    private boolean isValidEmail(String email) {
        String regex = "^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$";
        return email != null && email.matches(regex);
    }
}

6. 登錄控制器集成郵箱驗證碼

@RestController
@RequestMapping("/auth")
public class EmailLoginController {
    
    @Autowired
    private EmailCaptchaService emailCaptchaService;
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private JwtTokenProvider jwtTokenProvider;
    
    @PostMapping("/email-login")
    public ResponseEntity<?> emailLogin(@RequestBody EmailLoginRequest request) {
        String email = request.getEmail();
        String captcha = request.getCaptcha();
        
        // 驗證郵箱驗證碼
        if (!emailCaptchaService.validateEmailCaptcha(email, captcha)) {
            return ResponseEntity.badRequest().body(new ApiResponse(false, "驗證碼錯誤或已過期"));
        }
        
        // 查找或創(chuàng)建用戶
        User user = userService.findOrCreateByEmail(email);
        
        if (user != null) {
            // 生成JWT令牌
            String token = jwtTokenProvider.generateToken(user.getUsername());
            return ResponseEntity.ok(new JwtAuthResponse(token));
        } else {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ApiResponse(false, "登錄失敗,請稍后再試"));
        }
    }
}

優(yōu)缺點分析

優(yōu)點

  • 成本低廉,適合預(yù)算有限的項目
  • 無需依賴第三方服務(wù),可自行部署
  • 可發(fā)送富文本內(nèi)容,支持品牌定制
  • 適合注冊和找回密碼等非實時場景

缺點

  • 郵件可能被歸類為垃圾郵件或延遲送達
  • 用戶體驗不如短信驗證碼直接
  • 郵件服務(wù)器配置相對復(fù)雜
  • 不適合對實時性要求高的場景

方案四:基于AJ-Captcha的滑動拼圖驗證碼

原理介紹

滑動拼圖驗證碼是一種更現(xiàn)代的驗證方式,通過讓用戶拖動滑塊完成拼圖來驗證人機交互。這種驗證碼在用戶體驗和安全性之間取得了很好的平衡,既能有效防止自動化攻擊,又不會像傳統(tǒng)圖形驗證碼那樣影響用戶體驗。

實現(xiàn)步驟

1. 添加AJ-Captcha依賴

<dependency>
   <groupId>com.anji-plus</groupId>
   <artifactId>captcha-spring-boot-starter</artifactId>
   <version>1.4.0</version>
</dependency>

2. 配置滑動驗證碼參數(shù)

aj:
  captcha:
    cache-type: local
    expires-in: 300
    req-frequency-limit-count: 50
    cache-number: 1000
    jigsaw: classpath:images/jigsaw
    pic-click: classpath:images/pic-click

3. 自定義滑動驗證碼控制器

@RestController
@RequestMapping("/captcha")
public class JigsawCaptchaController {
    
    @Autowired
    private CaptchaService captchaService;
    
    @RequestMapping(value = "/get", method = {RequestMethod.GET, RequestMethod.POST})
    public ResponseModel get(@RequestParam(value = "type", defaultValue = "slide") String captchaType) {
        CaptchaVO captchaVO = new CaptchaVO();
        captchaVO.setCaptchaType(captchaType);
        return captchaService.get(captchaVO);
    }
    
    @PostMapping("/check")
    public ResponseModel check(@RequestBody CaptchaVO captchaVO) {
        return captchaService.check(captchaVO);
    }
}

4. 登錄控制器集成滑動驗證碼

@RestController
@RequestMapping("/auth")
public class JigsawLoginController {
    
    @Autowired
    private CaptchaService captchaService;

    @PostMapping("/jigsaw-login")
    public ResponseEntity<?> jigsawLogin(@RequestBody JigsawLoginRequest request) {
        // 驗證滑動驗證碼
        CaptchaVO captchaVO = new CaptchaVO();
        captchaVO.setCaptchaVerification(request.getCaptchaVerification());
        
        ResponseModel response = captchaService.verification(captchaVO);
        
        if (!response.isSuccess()) {
            return ResponseEntity.badRequest().body(new ApiResponse(false, "驗證碼校驗失敗"));
        }
        
        // TODO 模擬驗證用戶名密碼
        boolean authenticated = request.getPassword().equals("admin");
        
        if (authenticated) {
            // TODO 模擬生成令牌
            String token = IdUtil.simpleUUID();
            return ResponseEntity.ok(new JwtAuthResponse(token));
        } else {
            return ResponseEntity.badRequest().body(new ApiResponse(false, "用戶名或密碼錯誤"));
        }
    }
}
@SpringBootApplication
public class Main {

    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }

    @Bean
    public CaptchaService captchaService(){
        BlockPuzzleCaptchaServiceImpl clickWordCaptchaService = new BlockPuzzleCaptchaServiceImpl();
        Properties properties = new Properties();
        properties.setProperty(Const.CAPTCHA_FONT_TYPE,"WenQuanZhengHei.ttf");
        clickWordCaptchaService.init(properties);
        return clickWordCaptchaService;
    }

}

5. 前端集成示例

注:依賴的本地js文件可從 https://github.com/anji-plus/captcha/tree/master/view/html 獲取

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>滑動驗證碼登錄</title>
    <link rel="stylesheet" >
    <link rel="stylesheet" >
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/>
    <link rel="stylesheet" type="text/css" href="css/verify.css">
    <style>

    </style>
</head>
<body>
<div class="container mt-5">
    <div class="row justify-content-center">
        <div class="col-md-6">
            <div class="card">
                <div class="card-header">用戶登錄</div>
                <div class="card-body">
                    <form id="loginForm">
                        <div class="form-group">
                            <label for="username">用戶名</label>
                            <input type="text" class="form-control" id="username" name="username" required>
                        </div>
                        <div class="form-group">
                            <label for="password">密碼</label>
                            <input type="password" class="form-control" id="password" name="password" required>
                        </div>
                        <div class="form-group">
                            <div id="captcha"></div>
                            <input type="hidden" id="captchaVerification" name="captchaVerification">
                        </div>

                        <div id="slidePanel" ></div>

                        <button type="submit" class="btn btn-primary btn-block">登錄</button>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.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>
<script src="./js/crypto-js.js"></script>
<script src="./js/ase.js"></script>
<script src="./js/verify.js" ></script>
<script>
    let captchaVerification;
    // 初始化驗證碼  嵌入式
    $('#slidePanel').slideVerify({
        baseUrl:'http://localhost:8080',  // 服務(wù)器請求地址;
        mode:'fixed',
        imgSize : {       //圖片的大小對象
            width: '400px',
            height: '200px',
        },
        barSize:{
            width: '400px',
            height: '40px',
        },
        ready : function() {  //加載完畢的回調(diào)
        },
        success : function(params) { //成功的回調(diào)
            // 返回的二次驗證參數(shù) 合并到驗證通過之后的邏輯 參數(shù)中回傳服務(wù)器
            captchaVerification = params.captchaVerification;
        },
        error : function() {        //失敗的回調(diào)
        }
    });

    // 表單提交處理
    document.getElementById('loginForm').addEventListener('submit', function(e) {
        e.preventDefault();

        if (!captchaVerification) {
            alert('請先完成滑動驗證');
            return;
        }

        var data = {
            username: document.getElementById('username').value,
            password: document.getElementById('password').value,
            captchaVerification: captchaVerification
        };

        fetch('/auth/jigsaw-login', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(data)
        })
        .then(response => response.json())
        .then(data => {
            if (data.token) {
                localStorage.setItem('token', data.token);
                alert('login success');
            } else {
                alert(data.message);
            }
        })
        .catch(error => {
            console.error('Error:', error);
        });
    });
</script>
</body>
</html>

優(yōu)缺點分析

優(yōu)點

  • 用戶體驗良好,交互更加自然
  • 安全性較高,難以被自動化工具破解
  • 支持移動端和桌面端
  • 可定制化程度高,支持品牌化設(shè)計

缺點

  • 實現(xiàn)相對復(fù)雜,需要前后端配合
  • 依賴JavaScript,不支持純靜態(tài)環(huán)境

四種驗證碼方案對比

特性/方案圖形驗證碼短信驗證碼郵箱驗證碼滑動拼圖驗證碼
安全性中高
實現(xiàn)復(fù)雜度
適用場景簡單應(yīng)用高安全性需求注冊、找回密碼現(xiàn)代Web應(yīng)用
防機器人效果一般優(yōu)秀良好優(yōu)秀
是否需要第三方

總結(jié)

在實際項目中,可以根據(jù)應(yīng)用特點、用戶需求和安全要求選擇合適的驗證碼方案,甚至可以組合多種方案,在不同場景下使用不同的驗證方式,既保障系統(tǒng)安全,又提供良好的用戶體驗。

隨著AI技術(shù)的發(fā)展,驗證碼技術(shù)也在不斷演進。對于重要系統(tǒng),建議定期評估和更新驗證碼方案,以應(yīng)對新型的自動化攻擊手段。

到此這篇關(guān)于SpringBoot中登錄驗證碼的4種實現(xiàn)方案的文章就介紹到這了,更多相關(guān)springboot登錄驗證碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 如何使用JavaCV進行圖像灰度化處理

    如何使用JavaCV進行圖像灰度化處理

    在計算機視覺和圖像處理領(lǐng)域,圖像灰度化是一項基礎(chǔ)且重要的任務(wù),它將彩色圖像轉(zhuǎn)換為灰度圖像,JavaCV 是一個強大的開源庫,它提供了對各種計算機視覺算法和圖像處理操作的支持,本文將詳細(xì)介紹如何使用 JavaCV 進行圖像灰度化處理,需要的朋友可以參考下
    2024-10-10
  • 詳解eclipse創(chuàng)建maven項目實現(xiàn)動態(tài)web工程完整示例

    詳解eclipse創(chuàng)建maven項目實現(xiàn)動態(tài)web工程完整示例

    這篇文章主要介紹了詳解eclipse創(chuàng)建maven項目實現(xiàn)動態(tài)web工程完整示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • 如何在Java中使用標(biāo)準(zhǔn)庫創(chuàng)建臨時文件

    如何在Java中使用標(biāo)準(zhǔn)庫創(chuàng)建臨時文件

    有時候我們程序運行時需要產(chǎn)生中間文件,但是這些文件只是臨時用途,并不做長久保存,我們可以使用臨時文件,不需要長久保存,這篇文章主要給大家介紹了關(guān)于如何在Java中使用標(biāo)準(zhǔn)庫創(chuàng)建臨時文件的相關(guān)資料,需要的朋友可以參考下
    2023-10-10
  • Spring中@Qualifier注解的用法

    Spring中@Qualifier注解的用法

    但凡有點開發(fā)經(jīng)驗,多多少少可能都遇到過@Qualifier注解的使用場景,本文主要介紹了Spring中@Qualifier注解的用法,感興趣的可以了解一下
    2023-11-11
  • SpringBoot手動使用EhCache的方法示例

    SpringBoot手動使用EhCache的方法示例

    本篇文章主要介紹了SpringBoot手動使用EhCache的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-02-02
  • java實現(xiàn)簡單解析XML文件功能示例

    java實現(xiàn)簡單解析XML文件功能示例

    這篇文章主要介紹了java實現(xiàn)簡單解析XML文件功能,結(jié)合實例形式分析了java針對xml文件的讀取、遍歷節(jié)點及輸出等相關(guān)操作技巧,需要的朋友可以參考下
    2017-10-10
  • java8 stream多字段排序的實現(xiàn)

    java8 stream多字段排序的實現(xiàn)

    這篇文章主要介紹了java8 stream多字段排序的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Mybatis many=@Many的傳值問題解決

    Mybatis many=@Many的傳值問題解決

    本文詳細(xì)介紹了在MyBatis通過主查詢將單個或多個值傳遞給子查詢的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-11-11
  • SpringBoot整合Redis啟動失敗的常見錯誤以及解決方法

    SpringBoot整合Redis啟動失敗的常見錯誤以及解決方法

    本文詳細(xì)介紹了Spring Boot整合Redis啟動失敗的常見錯誤及其解決方法,涵蓋了服務(wù)配置、配置文件、依賴、集群配置、密碼、網(wǎng)絡(luò)、連接池、依賴沖突和Redis服務(wù)端等多個方面的問題,通過逐一排查和解決這些問題,需要的朋友可以參考下
    2025-11-11
  • java編程實現(xiàn)屏幕截圖(截屏)代碼總結(jié)

    java編程實現(xiàn)屏幕截圖(截屏)代碼總結(jié)

    這篇文章主要介紹了java編程實現(xiàn)屏幕截圖(截屏)代碼,結(jié)合3個實例總結(jié)分析了Java截屏?xí)r頁面抓取及圖片保存的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-11-11

最新評論

长兴县| 浦县| 孟连| 乌兰察布市| 大悟县| 哈密市| 龙里县| 桃园县| 丘北县| 盐亭县| 无棣县| 当阳市| 溧阳市| 雷山县| 滨海县| 乐山市| 台湾省| 黄浦区| 封丘县| 东源县| 永嘉县| 西乌珠穆沁旗| 喀喇沁旗| 正定县| 繁昌县| 临潭县| 曲沃县| 乌鲁木齐县| 松溪县| 贺兰县| 彭阳县| 平凉市| 中阳县| 龙口市| 会泽县| 南部县| 塔河县| 册亨县| 哈尔滨市| 辽宁省| 竹山县|