Spring?Boot實(shí)現(xiàn)登錄驗(yàn)證碼功能的案例詳解
驗(yàn)證碼的作用
驗(yàn)證碼的作用:可以有效防止其他人對(duì)某一個(gè)特定的注冊(cè)用戶(hù)用特定的程序暴力破解方式進(jìn)行不斷的登錄嘗試
我們其實(shí)很經(jīng)??吹剑卿浺恍┚W(wǎng)站其實(shí)是需要驗(yàn)證碼的,比如??停琎Q等。使用驗(yàn)證碼是現(xiàn)在很多網(wǎng)站通行的一種方式,這個(gè)問(wèn)題是由計(jì)算機(jī)生成并且評(píng)判的,但是必須只有人類(lèi)才能解答,因?yàn)橛?jì)算機(jī)無(wú)法解答驗(yàn)證碼的問(wèn)題,所以回答出問(wèn)題的用戶(hù)就可以被認(rèn)為是人類(lèi)。
驗(yàn)證碼一般用來(lái)防止批量注冊(cè)。
案例要求
驗(yàn)證碼本質(zhì):后端程序隨機(jī)驗(yàn)證碼
圖片的創(chuàng)建:
—java api手動(dòng)創(chuàng)建圖片
—JavaScript 前端創(chuàng)建圖片
驗(yàn)證碼的刷新
—添加JavaScript的點(diǎn)擊事件,重新請(qǐng)求驗(yàn)證碼圖片
前端頁(yè)面準(zhǔn)備
因?yàn)樯婕暗絡(luò)Query,所以需要在resources/static創(chuàng)建目錄,存放jQuery庫(kù)

準(zhǔn)備login.html頁(yè)面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用戶(hù)登錄</title>
<!--引入jQuery -->
<script type="text/javascript" src="/js/jquery-1.8.3.min.js"></script>
</head>
<body>
<h2>用戶(hù)登錄</h2>
<form action="/login" method="post">
用戶(hù)名:<input type="text" name="username"><br><br>
密碼 :<input type="password" name="password"><br><br>
驗(yàn)證碼:<input id="identify-input" type="text" name="identifyCode">
<img id="identify-img" src="/identifyImage"><br><br>
<input type="submit" value="登錄">
</form>
<!--綁定點(diǎn)擊事件 -->
<script>
$("#identify-img").on('click',function (){
// 點(diǎn)擊驗(yàn)證碼那個(gè)圖片的時(shí)候,我們輸入的驗(yàn)證碼那個(gè)框就會(huì)清空
$('#identify-input').val('')
//而且我們點(diǎn)擊驗(yàn)證碼的時(shí)候,希望它可以改變驗(yàn)證碼內(nèi)容,其實(shí)是通過(guò)發(fā)送新請(qǐng)求來(lái)改變驗(yàn)證碼內(nèi)容
$('#identify-img').attr('src','/identifyImage?'+Math.random())
})
</script>
</body>
</html>
隨機(jī)驗(yàn)證碼工具類(lèi)
public class IdentifyCodeUtils {
//設(shè)置圖片寬
private int width = 95;
//設(shè)置圖片高度
private int height = 25;
//設(shè)置干擾線數(shù)量
private int lineSize = 40;
//隨機(jī)產(chǎn)生數(shù)字和字母組合的字符串
private String randString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private Random random = new Random();
/**
* 獲得字體
*/
private Font getFont() {
return new Font("Fixedsys", Font.CENTER_BASELINE, 18);
}
/**
* 獲得顏色
*/
private Color getRandColor(int fc, int bc) {
if (fc > 255) {
fc = 255;
}
if (bc > 255) {
bc = 255;
}
int r = fc + random.nextInt(bc - fc - 16);
int g = fc + random.nextInt(bc - fc - 14);
int b = fc + random.nextInt(bc - fc - 18);
return new Color(r, g, b);
}
/**
* 獲取驗(yàn)證碼
*
* @return
*/
public String getIdentifyCode() {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < 4; i++) {
char c = randString.charAt(random.nextInt(randString.length()));
buffer.append(c);
}
return buffer.toString();
}
/**
* 生成隨機(jī)圖片
*
* @param identifyCode
* @return
*/
public BufferedImage getIdentifyImage(String identifyCode) {
//BufferedImage類(lèi)是具有緩沖區(qū)的Image類(lèi),Image類(lèi)是用來(lái)描述圖像信息的類(lèi)
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
//產(chǎn)生Image對(duì)象的Graphics對(duì)象,改對(duì)象可以在圖像上進(jìn)行各種繪制操作
Graphics graphics = image.getGraphics();
//圖片大小
graphics.fillRect(0, 0, width, height);
//字體大小
graphics.setFont(new Font("Times New Roman", Font.ROMAN_BASELINE, 18));
//字體顏色
graphics.setColor(getRandColor(110, 133));
//繪制干擾線
for (int i = 0; i <= lineSize; i++) {
drawLine(graphics);
}
//繪制隨機(jī)字符
drawString(graphics, identifyCode);
graphics.dispose();
return image;
}
/**
* 繪制字符串
*/
private void drawString(Graphics g, String identifyCode) {
for (int i = 0; i < identifyCode.length(); i++) {
g.setFont(getFont());
g.setColor(new Color(random.nextInt(101), random.nextInt(111), random
.nextInt(121)));
g.translate(random.nextInt(3), random.nextInt(3));
g.drawString(String.valueOf(identifyCode.charAt(i)), 13 * i + 20, 18);
}
}
/**
* 響應(yīng)驗(yàn)證碼圖片
*
* @param identifyImg
* @param response
*/
public void responseIdentifyImg(BufferedImage identifyImg, HttpServletResponse response) {
//設(shè)置響應(yīng)類(lèi)型,告訴瀏覽器輸出的內(nèi)容是圖片
response.setContentType("image/jpeg");
//設(shè)置響應(yīng)頭信息,告訴瀏覽器不用緩沖此內(nèi)容
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expire", 0);
try {
//把內(nèi)存中的圖片通過(guò)流動(dòng)形式輸出到客戶(hù)端
ImageIO.write(identifyImg, "JPEG", response.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 繪制干擾線
*/
private void drawLine(Graphics graphics) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(13);
int yl = random.nextInt(15);
graphics.drawLine(x, y, x + xl, y + yl);
}
}后端控制器
@Controller
public class UserController {
@RequestMapping("/loginShow")
public String loginShow(){
return "login.html";
}
@PostMapping("/login")
public String login(String username,String password,String identifyCode,HttpSession session){
System.out.println("用戶(hù)名:"+username);
System.out.println("密碼:"+password);
System.out.println("驗(yàn)證碼:"+identifyCode);
//從session中取出驗(yàn)證碼
String sessionCode = (String)session.getAttribute("identifyFyCode");
if (identifyCode.equalsIgnoreCase(sessionCode)){
System.out.println("驗(yàn)證碼正確");
//進(jìn)行登錄判斷的邏輯大家自己寫(xiě),這里就不演示了
}else{
System.out.println("驗(yàn)證碼錯(cuò)誤");
//重定向到登錄畫(huà)面
return "redirect:/loginShow";
}
return "";
}
/**
* 給前端返回一個(gè)驗(yàn)證碼圖片
* @return
*/
@RequestMapping("/identifyImage")
public void identifyImage(HttpServletResponse response, HttpSession session){
//創(chuàng)建隨機(jī)驗(yàn)證碼
IdentifyCodeUtils utils = new IdentifyCodeUtils();
String identifyCode = utils.getIdentifyCode();
//session存入驗(yàn)證碼
session.setAttribute("identifyCode", identifyCode);
//根據(jù)驗(yàn)證碼創(chuàng)建圖片
BufferedImage identifyImage = utils.getIdentifyImage(identifyCode);
//回傳給前端
utils.responseIdentifyImg(identifyImage,response);
}
}
測(cè)試

當(dāng)我們點(diǎn)擊驗(yàn)證碼這個(gè)圖片的時(shí)候,它就會(huì)生成新驗(yàn)證碼
并且如果我們?cè)谳斎肟蛑腥绻袑?xiě)驗(yàn)證碼的話,當(dāng)我們點(diǎn)擊驗(yàn)證碼圖片,它就會(huì)把輸入框內(nèi)容清空(大家自己測(cè)試)
到此這篇關(guān)于Spring Boot實(shí)現(xiàn)登錄驗(yàn)證碼功能的案例詳解的文章就介紹到這了,更多相關(guān)springboot登錄驗(yàn)證碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JAVA多線程和并發(fā)基礎(chǔ)面試問(wèn)答(翻譯)
多線程和并發(fā)問(wèn)題是Java技術(shù)面試中面試官比較喜歡問(wèn)的問(wèn)題之一。在這里,從面試的角度列出了大部分重要的問(wèn)題,但是你仍然應(yīng)該牢固的掌握J(rèn)ava多線程基礎(chǔ)知識(shí)來(lái)對(duì)應(yīng)日后碰到的問(wèn)題2014-09-09
一文了解Java動(dòng)態(tài)代理的原理及實(shí)現(xiàn)
動(dòng)態(tài)代理指的是,代理類(lèi)和目標(biāo)類(lèi)的關(guān)系在程序運(yùn)行的時(shí)候確定的,客戶(hù)通過(guò)代理類(lèi)來(lái)調(diào)用目標(biāo)對(duì)象的方法,是在程序運(yùn)行時(shí)根據(jù)需要?jiǎng)討B(tài)的創(chuàng)建目標(biāo)類(lèi)的代理對(duì)象。本文將通過(guò)案例詳細(xì)講解一下Java動(dòng)態(tài)代理的原理及實(shí)現(xiàn),需要的可以參考一下2022-07-07
JDK源碼之線程并發(fā)協(xié)調(diào)神器CountDownLatch和CyclicBarrier詳解
我一直認(rèn)為程序是對(duì)于現(xiàn)實(shí)世界的邏輯描述,而在現(xiàn)實(shí)世界中很多事情都需要各方協(xié)調(diào)合作才能完成,就好比完成一個(gè)平臺(tái)的交付不可能只靠一個(gè)人,而需要研發(fā)、測(cè)試、產(chǎn)品以及項(xiàng)目經(jīng)理等不同角色人員進(jìn)行通力合作才能完成最終的交付2022-02-02
springboot整合shiro之thymeleaf使用shiro標(biāo)簽的方法
Thymeleaf 是一個(gè)跟 Velocity、FreeMarker 類(lèi)似的模板引擎,它可以完全替代 JSP ,這篇文章主要介紹了springboot整合shiro之thymeleaf使用shiro標(biāo)簽的相關(guān)知識(shí),需要的朋友可以參考下2021-10-10
為什么程序中突然多了 200 個(gè) Dubbo-thread 線程的說(shuō)明
這篇文章主要介紹了為什么程序中突然多了 200 個(gè) Dubbo-thread 線程的說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09
Java實(shí)現(xiàn)替換Word中文本和圖片功能
Word中的替換功能以查找指定文本然后替換為新的文本,可單個(gè)替換或全部替換。本文將用Java語(yǔ)言實(shí)現(xiàn)Word中的文本、圖片替換功能,需要的可以參考一下2022-06-06
java網(wǎng)絡(luò)編程學(xué)習(xí)java聊天程序代碼分享
java聊天程序代碼分享,大家參考使用吧2013-12-12
springboot使用線程池(ThreadPoolTaskExecutor)示例
大家好,本篇文章主要講的是springboot使用線程池(ThreadPoolTaskExecutor)示例,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12

