Java使用hutool工具實(shí)現(xiàn)驗(yàn)證碼登錄
1.先說(shuō)一下流程圖

2.導(dǎo)入工具包
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.12</version>
</dependency>
3.流程梳理
3.1前端模版代碼
<template>
<form @submit.prevent="handleSubmit">
<div class="input-group">
<div class="captcha-wrapper">
<input
v-model="form.captcha"
type="text"
placeholder="請(qǐng)輸入驗(yàn)證碼"
:class="{ 'shake': formErrors.captcha }"
maxlength="4"
/>
<div class="captcha-container">
<img
:src="captchaUrl"
@click="refreshCaptcha"
class="captcha-img"
alt="驗(yàn)證碼"
@error="handleCaptchaError"
/>
<button
type="button"
class="refresh-btn"
@click="refreshCaptcha"
title="刷新驗(yàn)證碼"
>
</button>
</div>
</div>
<transition name="fade">
<p v-if="formErrors.captcha" class="error-message">{{ formErrors.captcha }}</p>
</transition>
</template>3.2邏輯代碼
前端掛載組件時(shí)發(fā)送請(qǐng)求到后端生成驗(yàn)證碼
@RestController
@RequestMapping("/api")
public class AuthController {
@GetMapping("/captcha")
public void generateCaptcha(HttpServletResponse response, HttpSession session) {
// 生成驗(yàn)證碼
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(100, 40);
// 將驗(yàn)證碼存入session
session.setAttribute("captcha", lineCaptcha.getCode());
try {
// 輸出到客戶端
response.setContentType("image/png");
lineCaptcha.write(response.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
<img :src="captchaUrl”>動(dòng)態(tài)綁定實(shí)現(xiàn)渲染驗(yàn)證碼,button綁定的是這個(gè)refreshCaptcha事件,所以可以實(shí)現(xiàn)刷新驗(yàn)證碼的操作
const captchaUrl = ref('')
// 組件掛載時(shí)加載驗(yàn)證碼
onMounted(() => {
refreshCaptcha()
})
const refreshCaptcha = () => {
// 直接使用API地址,添加時(shí)間戳防止緩存
captchaUrl.value = `${request.defaults.baseURL}/api/captcha?t=${new Date().getTime()}`
// 清空驗(yàn)證碼輸入
form.captcha = ''
}
然后就是前段提交數(shù)據(jù)到后端做認(rèn)證
const form = reactive({
email: '',
password: '',
remember: false,
captcha: ''
})
export function login(data) {
return request({
url: '/api/login',
method: 'post',
data
})
}
try {
// 發(fā)送登錄請(qǐng)求
const res = await login({
username: form.email,
password: form.password,
captcha: form.captcha,
remember: form.remember
})后端接收數(shù)據(jù)進(jìn)行校驗(yàn)
@PostMapping("/login")
public Result login(@RequestBody LoginDTO loginDTO, HttpSession session) {
// 獲取session中的驗(yàn)證碼
String captcha = (String) session.getAttribute("captcha");
// 校驗(yàn)驗(yàn)證碼
if (!loginDTO.getCaptcha().equalsIgnoreCase(captcha)) {
return Result.error("驗(yàn)證碼錯(cuò)誤");
}
// TODO: 進(jìn)行登錄邏輯處理
return null;
}
4.前端所有代碼
<template>
<div class="login-page">
<div class="login-container">
<!-- Left side with rocket -->
<div class="left-side">
<div class="logo-container">
<h1 class="logo">七禾頁(yè)話</h1>
</div>
<div class="hero-text">
<h2>歡迎使用<br />學(xué)生管理系統(tǒng)</h2>
<p>讓工作更高效!</p>
</div>
<!-- Animated rocket -->
<div class="rocket-container">
<div class="rocket" :class="{ 'rocket-hover': isRocketHovering }">
<div class="rocket-body">
<div class="rocket-main"></div>
<div class="rocket-base"></div>
<div class="rocket-side-left"></div>
<div class="rocket-side-right"></div>
</div>
</div>
<!-- Animated clouds -->
<div class="clouds">
<div v-for="i in 3" :key="i"
class="cloud"
:class="`cloud-${i}`"
:style="`--delay: ${i * 2}s`">
</div>
</div>
</div>
</div>
<!-- Right side with form -->
<div class="right-side">
<div class="form-container">
<h2>用戶登錄</h2>
<form @submit.prevent="handleSubmit">
<div class="input-group">
<input
v-model="form.email"
type="email"
placeholder="請(qǐng)輸入用戶名"
:class="{ 'shake': formErrors.email }"
/>
<transition name="fade">
<p v-if="formErrors.email" class="error-message">{{ formErrors.email }}</p>
</transition>
</div>
<div class="input-group">
<input
v-model="form.password"
type="password"
placeholder="請(qǐng)輸入密碼"
:class="{ 'shake': formErrors.password }"
/>
<transition name="fade">
<p v-if="formErrors.password" class="error-message">{{ formErrors.password }}</p>
</transition>
</div>
<div class="input-group">
<div class="captcha-wrapper">
<input
v-model="form.captcha"
type="text"
placeholder="請(qǐng)輸入驗(yàn)證碼"
:class="{ 'shake': formErrors.captcha }"
maxlength="4"
/>
<div class="captcha-container">
<img
:src="captchaUrl"
@click="refreshCaptcha"
class="captcha-img"
alt="驗(yàn)證碼"
@error="handleCaptchaError"
/>
<button
type="button"
class="refresh-btn"
@click="refreshCaptcha"
title="刷新驗(yàn)證碼"
>
<svg viewBox="0 0 24 24" class="refresh-icon">
<path d="M17.65 6.35A7.958 7.958 0 0012 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0112 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z" fill="currentColor"/>
</svg>
</button>
</div>
</div>
<transition name="fade">
<p v-if="formErrors.captcha" class="error-message">{{ formErrors.captcha }}</p>
</transition>
</div>
<div class="remember-me">
<input
v-model="form.remember"
type="checkbox"
id="remember"
/>
<label for="remember">記住我</label>
</div>
<button
type="submit"
:class="{ 'loading': isLoading }"
:disabled="isLoading"
>
<span v-if="!isLoading">登 錄</span>
<span v-else class="loading-text">
<svg class="spinner" viewBox="0 0 50 50">
<circle class="path" cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
</svg>
登錄中...
</span>
</button>
</form>
<div class="auth-links">
<p class="forgot-password">
<a href="#">忘記密碼?</a>
</p>
<p class="register-link">
還沒有賬號(hào)? <router-link to="/register" class="text-primary">立即注冊(cè)</router-link>
</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { login, getCaptcha } from '@/api/auth'
import request from '@/utils/request'
const router = useRouter()
const isRocketHovering = ref(true)
const isLoading = ref(false)
const captchaUrl = ref('')
const form = reactive({
email: '',
password: '',
remember: false,
captcha: ''
})
const formErrors = reactive({
email: '',
password: '',
captcha: ''
})
// 刷新驗(yàn)證碼
const refreshCaptcha = () => {
// 直接使用API地址,添加時(shí)間戳防止緩存
captchaUrl.value = `${request.defaults.baseURL}/api/captcha?t=${new Date().getTime()}`
// 清空驗(yàn)證碼輸入
form.captcha = ''
}
// 處理驗(yàn)證碼加載錯(cuò)誤
const handleCaptchaError = () => {
console.error('驗(yàn)證碼圖片加載失敗')
// 可以在這里添加重試邏輯或顯示錯(cuò)誤提示
}
// 組件掛載時(shí)加載驗(yàn)證碼
onMounted(() => {
refreshCaptcha()
})
const handleSubmit = async () => {
// Reset errors
formErrors.email = ''
formErrors.password = ''
formErrors.captcha = ''
// Validate
if (!form.email) {
formErrors.email = '請(qǐng)輸入用戶名'
return
}
if (!form.password) {
formErrors.password = '請(qǐng)輸入密碼'
return
}
if (!form.captcha) {
formErrors.captcha = '請(qǐng)輸入驗(yàn)證碼'
return
}
// Show loading state
isLoading.value = true
try {
// 發(fā)送登錄請(qǐng)求
const res = await login({
username: form.email,
password: form.password,
captcha: form.captcha,
remember: form.remember
})
// 存儲(chǔ) token
localStorage.setItem('token', res.data.token)
// 登錄成功,跳轉(zhuǎn)到首頁(yè)
router.push('/dashboard')
} catch (error) {
// 根據(jù)錯(cuò)誤類型顯示不同的錯(cuò)誤信息
if (error.code === 400) {
formErrors.captcha = '驗(yàn)證碼錯(cuò)誤'
await refreshCaptcha()
} else if (error.code === 401) {
formErrors.password = '用戶名或密碼錯(cuò)誤'
await refreshCaptcha()
} else {
console.error('登錄失敗:', error)
formErrors.password = error.message || '登錄失敗,請(qǐng)稍后重試'
await refreshCaptcha()
}
} finally {
// Reset loading state
isLoading.value = false
}
}
// Start rocket hover animation
setInterval(() => {
isRocketHovering.value = !isRocketHovering.value
}, 2000)
</script>
5.后端代碼
package com.qiheyehua.vuespringboot2.controller;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import com.qiheyehua.vuespringboot2.domain.dto.LoginDTO;
import com.qiheyehua.vuespringboot2.utils.Result;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
/**
* @author 七禾頁(yè)話
* @date 2024/12/16 18:57
**/
@RestController
@RequestMapping("/api")
public class AuthController {
@GetMapping("/captcha")
public void generateCaptcha(HttpServletResponse response, HttpSession session) {
// 生成驗(yàn)證碼
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(100, 40);
// 將驗(yàn)證碼存入session
session.setAttribute("captcha", lineCaptcha.getCode());
try {
// 輸出到客戶端
response.setContentType("image/png");
lineCaptcha.write(response.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
@PostMapping("/login")
public Result login(@RequestBody LoginDTO loginDTO, HttpSession session) {
// 獲取session中的驗(yàn)證碼
String captcha = (String) session.getAttribute("captcha");
// 校驗(yàn)驗(yàn)證碼
if (!loginDTO.getCaptcha().equalsIgnoreCase(captcha)) {
return Result.error("驗(yàn)證碼錯(cuò)誤");
}
// TODO: 進(jìn)行登錄邏輯處理
return null;
}
}到此這篇關(guān)于Java使用hutool工具實(shí)現(xiàn)驗(yàn)證碼登錄的文章就介紹到這了,更多相關(guān)Java hutool驗(yàn)證碼登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Hibernate分頁(yè)的兩種實(shí)現(xiàn)方法
這篇文章主要介紹了Hibernate分頁(yè)的兩種實(shí)現(xiàn)方法,結(jié)合實(shí)例形式講述了criteria分頁(yè)與hql分頁(yè)的實(shí)現(xiàn)方法,需要的朋友可以參考下2016-03-03
java開發(fā)工作中對(duì)InheritableThreadLocal使用思考
這篇文章主要為大家介紹了java開發(fā)工作中對(duì)InheritableThreadLocal使用思考詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
關(guān)于FileChannel的transferFrom()方法的使用及說(shuō)明
這篇文章主要介紹了關(guān)于FileChannel的transferFrom()方法的使用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-05-05
spring?boot前后端交互之?dāng)?shù)據(jù)格式轉(zhuǎn)換問(wèn)題
這篇文章主要介紹了spring?boot前后端交互之?dāng)?shù)據(jù)格式轉(zhuǎn)換,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01
Spring?Boot?項(xiàng)目中?JPA?語(yǔ)法的基本使用方法
這篇文章主要介紹了?Spring?Boot?項(xiàng)目中?JPA?語(yǔ)法的基本使用方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-10-10
Java代理模式之靜態(tài)代理與動(dòng)態(tài)代理詳解
Java代理分為靜態(tài)代理和動(dòng)態(tài)代理,靜態(tài)代理需手動(dòng)編寫代理類,適合少量對(duì)象,動(dòng)態(tài)代理運(yùn)行時(shí)自動(dòng)生成代理類,適用于大量對(duì)象,如Spring AOP,這篇文章主要介紹了Java代理模式之靜態(tài)代理與動(dòng)態(tài)代理的相關(guān)資料,需要的朋友可以參考下2026-01-01
我用java實(shí)現(xiàn)了王者榮耀的皮膚和英雄技能
上篇文章主要實(shí)現(xiàn)了創(chuàng)建英雄,創(chuàng)建野怪,創(chuàng)建裝備.并且實(shí)現(xiàn)了簡(jiǎn)單的刷怪,購(gòu)買裝備等.本篇文章我優(yōu)化了我的操作界面,并且實(shí)現(xiàn)了英雄技能,英雄皮膚等,需要的朋友可以參考下2021-05-05
Java Hibernate中的持久化類和實(shí)體類關(guān)系
Hibernate是一種Java對(duì)象關(guān)系映射框架,通過(guò)持久化類將Java對(duì)象映射到數(shù)據(jù)庫(kù)表中。持久化類需要實(shí)現(xiàn)無(wú)參構(gòu)造器、具有標(biāo)識(shí)屬性和使用注解或XML進(jìn)行映射。Hibernate通過(guò)Session來(lái)管理對(duì)象的狀態(tài),包括臨時(shí)狀態(tài)、持久化狀態(tài)和游離狀態(tài)2023-04-04

