引入QQ郵箱發(fā)送驗(yàn)證碼進(jìn)行安全校驗(yàn)功能實(shí)現(xiàn)
最近想給自己的項(xiàng)目在注冊(cè)時(shí)加點(diǎn)安全校驗(yàn),本想著使用短信驗(yàn)證碼,奈何囊中羞澀只能退而求次改用QQ郵箱驗(yàn)證注冊(cè)~
一.需求分析
- 場(chǎng)景:用戶輸入自己的郵箱,點(diǎn)擊獲取驗(yàn)證碼,后臺(tái)會(huì)發(fā)送一封郵件到對(duì)應(yīng)郵箱中。
- 分析:防止刷爆郵箱,可以限制一分鐘內(nèi)只能獲取一次。
- 前端:期限內(nèi)禁用button按鈕。
- 后端:存入redis設(shè)置過(guò)期時(shí)間,請(qǐng)求先判斷redis中是否有數(shù)據(jù)。
二.環(huán)境準(zhǔn)備
(1) 郵箱環(huán)境
在QQ郵箱中開啟SMTP服務(wù),獲取授權(quán)碼
1.網(wǎng)頁(yè)版:進(jìn)入郵箱,點(diǎn)擊設(shè)置中的賬戶

2.往下翻可以看到如下服務(wù)開關(guān),點(diǎn)擊開啟

點(diǎn)擊開啟后會(huì)得到一串授權(quán)碼,后端程序中需要用到。
3.可能會(huì)要求完成相關(guān)安全驗(yàn)證

(2) 后端環(huán)境
大概率是在web項(xiàng)目中使用到,因此我們創(chuàng)建一個(gè)SpringBoot工程
1.創(chuàng)建好項(xiàng)目后在pom文件中導(dǎo)入操作郵箱所需jar包
<!--QQ郵箱驗(yàn)證碼所需jar包-->
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
<version>1.4</version>
</dependency>2.由于我們需要在spring項(xiàng)目使用redis緩存驗(yàn)證碼因此還要導(dǎo)入redis的jar包
<!-- 使用redis緩存驗(yàn)證碼時(shí)效-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>3.在yml文件中配置redis,設(shè)置了redis密碼記得加上密碼配置
spring:
redis:
# redis數(shù)據(jù)庫(kù)索引(默認(rèn)為0),我們使用索引為3的數(shù)據(jù)庫(kù),避免和其他數(shù)據(jù)庫(kù)沖突
database: 3
# redis服務(wù)器地址(默認(rèn)為localhost)
host: localhost
# redis端口(默認(rèn)為6379)
port: 6379三.后端程序
(1) 效果實(shí)現(xiàn)
1.發(fā)送郵箱應(yīng)該算個(gè)工具,因此我們可以在工具類中寫入如下代碼
package com.example.utils;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.SimpleEmail;
public class SendMailUtil {
/**
* 發(fā)送郵件代碼
*
* @param targetEmail 目標(biāo)用戶郵箱
* @param authCode 發(fā)送的驗(yàn)證碼
*/
public static void sendEmailCode(String targetEmail, String authCode) {
try {
// 創(chuàng)建郵箱對(duì)象
SimpleEmail mail = new SimpleEmail();
// 設(shè)置發(fā)送郵件的服務(wù)器
mail.setHostName("smtp.qq.com");
// "你的郵箱號(hào)"+ "上文開啟SMTP獲得的授權(quán)碼"
mail.setAuthentication("158xxx69@qq.com", "fbsxxxxxsijdj");
// 發(fā)送郵件 "你的郵箱號(hào)"+"發(fā)送時(shí)用的昵稱"
mail.setFrom("15xxx69@qq.com", "觀止");
// 使用安全鏈接
mail.setSSLOnConnect(true);
// 接收用戶的郵箱
mail.addTo(targetEmail);
// 郵件的主題(標(biāo)題)
mail.setSubject("注冊(cè)驗(yàn)證碼");
// 郵件的內(nèi)容
mail.setMsg("您的驗(yàn)證碼為:" + authCode+"(一分鐘內(nèi)有效)");
// 發(fā)送
mail.send();
} catch (EmailException e) {
e.printStackTrace();
}
}
}
2.編寫如下接口
@RestController
public class SendMail {
@PostMapping("/getCode")
@ResponseBody
public String mail(@RequestParam("targetEmail") String targetEmail) {
// 隨機(jī)生成六位數(shù)驗(yàn)證碼
String authCode = String.valueOf(new Random().nextInt(899999) + 100000);
SendMailUtil.sendEmailCode(targetEmail,authCode);
return "ok";
}
}3.讓我們測(cè)試一下接口
GET http://localhost:8080/getCode?targetEmail=35xxxx947@qq.com
可以看到如下效果:

如此我們初步效果就已經(jīng)實(shí)現(xiàn)啦~
(2) 緩存改進(jìn)
上述程序我們瘋狂發(fā)送請(qǐng)求可以一直發(fā)送郵箱,這顯然不是我們所期待的,接下來(lái)我們加入redis來(lái)改進(jìn)一下。
@RestController
public class SendMail {
@Resource
private RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
/**
* @param targetEmail 用戶郵箱
* @return
*/
@GetMapping("/getCode")
@ResponseBody
public String mail(@RequestParam("targetEmail") String targetEmail) {
// 發(fā)送前先看下我們是否已經(jīng)緩存了驗(yàn)證碼
String yzm = redisTemplate.opsForValue().get("yzm");
// 判斷是否存在
if (yzm == null){
// 生成六位數(shù)驗(yàn)證碼
int authNum = new Random().nextInt(899999) + 100000;
String authCode = String.valueOf(authNum);
// 不存在,我們發(fā)送郵箱給用戶
SendMailUtil.sendEmailCode(targetEmail, "你的驗(yàn)證碼為:" + authCode + "(五分鐘內(nèi)有效)");
// 存入redis中,設(shè)置有效期為1分鐘
redisTemplate.opsForValue().set("yzm", authCode, 1, TimeUnit.MINUTES);
return "發(fā)送成功";
}
// 存在,直接返回,不再發(fā)送郵箱~
return "請(qǐng)勿重復(fù)發(fā)送驗(yàn)證碼";
}
}如此再次測(cè)試,可以發(fā)現(xiàn)瘋狂點(diǎn)擊不再產(chǎn)生效果,成功被攔截,如此安全了許多

至此我們開始想要的效果便已經(jīng)在小demo中實(shí)現(xiàn)了,接下來(lái)可以引入正式自己項(xiàng)目啦
四.前端(補(bǔ)充)
用原生js簡(jiǎn)單寫了一個(gè)界面,感興趣的可以看一看

代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<input id="mail" type="text">
<button id="getCode">獲取驗(yàn)證碼</button>
</div>
<script>
/*按鈕禁用60秒,并顯示倒計(jì)時(shí)*/
function disabledButton() {
const getCode = document.querySelector("#getCode")
getCode.disabled = true
let second = 60;
const intervalObj = setInterval(function () {
getCode.innerText = "請(qǐng)" + second + "秒后再重試"
if (second === 0) {
getCode.innerText = "獲取驗(yàn)證碼"
getCode.disabled = false
clearInterval(intervalObj);
}
second--;
}, 1000);
}
document.querySelector("#getCode").addEventListener('click', function () {
const mail = document.querySelector("#mail")
let xhr = new XMLHttpRequest();
xhr.open("GET", "http://localhost:8080/getCode?targetEmail=" + mail.value, true);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
alert(xhr.response);
disabledButton()
}
}
})
</script>
</body>
</html>到此這篇關(guān)于引入QQ郵箱發(fā)送驗(yàn)證碼進(jìn)行安全校驗(yàn)的文章就介紹到這了,更多相關(guān)QQ郵箱發(fā)送驗(yàn)證碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中CompletableFuture?的詳細(xì)介紹
這篇文章主要介紹了Java中的CompletableFuture,通過(guò)創(chuàng)建?CompletableFuture?的對(duì)象的工廠方法展開詳細(xì)的內(nèi)容介紹,需要的小伙伴可以參考一下2022-05-05
Java lambda表達(dá)式實(shí)現(xiàn)Flink WordCount過(guò)程解析
這篇文章主要介紹了Java lambda表達(dá)式實(shí)現(xiàn)Flink WordCount過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02
java向es中寫入數(shù)據(jù)報(bào)錯(cuò)org.elasticsearch.action.ActionReque問(wèn)題
這篇文章主要介紹了java向es中寫入數(shù)據(jù)報(bào)錯(cuò)org.elasticsearch.action.ActionReque問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
如何解決java.util.zip.ZipFile解壓后被java占用問(wèn)題
這篇文章主要介紹了如何解決java.util.zip.ZipFile解壓后被java占用問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06
Springboot @Transactional大事務(wù)處理的幾點(diǎn)建議
本文主要介紹了大事務(wù)的概念及其危害,并提出了幾種解決大事務(wù)問(wèn)題的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-01-01
Java使用NIO包實(shí)現(xiàn)Socket通信的實(shí)例代碼
本篇文章主要介紹了Java使用NIO包實(shí)現(xiàn)Socket通信的實(shí)例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02
詳解Java中NullPointerException異常的原因和解決辦法
本文主要介紹了詳解Java中NullPointerException異常的原因和解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
解決mybatis竟然報(bào)Invalid value for getInt()的問(wèn)題
使用mybatis遇到一個(gè)非常奇葩的問(wèn)題,總是報(bào)Invalid value for getInt()的問(wèn)題,怎么解決呢?下面小編通過(guò)場(chǎng)景分析給大家代來(lái)了mybatis報(bào)Invalid value for getInt()的解決方法,感興趣的朋友參考下吧2021-10-10

