Outras soluções => Introduzir o serviço SMS para enviar o código de verificação do celular para verificação de segurança.
A operação é relativamente complicada e cobrada. Tutoriais detalhados estão disponíveis para referência.
Recentemente, quero adicionar algumas verificações de segurança ao meu projeto ao me registrar. Originalmente, queria usar o código de verificação por SMS, mas só posso recuar e usar a caixa de correio QQ para verificar o registro~
1. Análise de demanda
-
Cenário: O usuário insere seu e-mail, clica para obter o código de verificação e o segundo plano enviará um e-mail para o e-mail correspondente.
-
Análise: para evitar o estouro das caixas de correio, você pode limitar o acesso a apenas uma vez por minuto.
- Frontend: desative o botão de botão dentro do limite de tempo.
- Back-end: armazene em redis para definir o tempo de expiração e solicite primeiro para determinar se há dados em redis.
2. Preparação ambiental
(1) Ambiente de caixa de correio
Abra o serviço SMTP na caixa de correio QQ e obtenha o código de autorização
-
Versão da Web: entre na caixa de correio, clique na conta nas configurações
-
Role para baixo para ver a seguinte opção de serviço, clique para abrir
Depois de clicar em Abrir, você receberá uma sequência de códigos de autorização, que precisam ser usados no programa de back-end.
- Pode ser necessário concluir a verificação de segurança relevante
(2) Ambiente de back-end
A alta probabilidade é usada em projetos da web, então criamos um projeto SpringBoot
- Depois de criar o projeto, importe o pacote jar necessário para operar a caixa de correio no arquivo pom
<!--QQ邮箱验证码所需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>
- Como precisamos usar o código de verificação do cache redis no projeto spring, também precisamos importar o pacote redis jar
<!-- 使用redis缓存验证码时效-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- Configure o redis no arquivo yml, defina a senha do redis e lembre-se de adicionar a configuração da senha
spring:
redis:
# redis数据库索引(默认为0),我们使用索引为3的数据库,避免和其他数据库冲突
database: 3
# redis服务器地址(默认为localhost)
host: localhost
# redis端口(默认为6379)
port: 6379
3. Programa de back-end
(1) Realização do efeito
- O envio de e-mail deve ser considerado uma ferramenta, então podemos escrever o seguinte código na classe de ferramenta
package com.example.utils;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.SimpleEmail;
public class SendMailUtil {
/**
* 发送邮件代码
*
* @param targetEmail 目标用户邮箱
* @param authCode 发送的验证码
*/
public static void sendEmailCode(String targetEmail, String authCode) {
try {
// 创建邮箱对象
SimpleEmail mail = new SimpleEmail();
// 设置发送邮件的服务器
mail.setHostName("smtp.qq.com");
// "你的邮箱号"+ "上文开启SMTP获得的授权码"
mail.setAuthentication("[email protected]", "fbsxxxxxsijdj");
// 发送邮件 "你的邮箱号"+"发送时用的昵称"
mail.setFrom("[email protected]", "观止");
// 使用安全链接
mail.setSSLOnConnect(true);
// 接收用户的邮箱
mail.addTo(targetEmail);
// 邮件的主题(标题)
mail.setSubject("注册验证码");
// 邮件的内容
mail.setMsg("您的验证码为:" + authCode+"(一分钟内有效)");
// 发送
mail.send();
} catch (EmailException e) {
e.printStackTrace();
}
}
}
- Escreva a seguinte interface
@RestController
public class SendMail {
@PostMapping("/getCode")
@ResponseBody
public String mail(@RequestParam("targetEmail") String targetEmail) {
// 随机生成六位数验证码
String authCode = String.valueOf(new Random().nextInt(899999) + 100000);
SendMailUtil.sendEmailCode(targetEmail,authCode);
return "ok";
}
}
- Vamos testar a interface
GET http://localhost:8080/[email protected]
Você pode ver os seguintes efeitos:
Desta forma, nosso efeito inicial foi realizado~
(3) Melhoria do cache
No programa acima, podemos enviar e-mails o tempo todo enviando solicitações loucamente. Obviamente, isso não é o que esperávamos. Em seguida, adicionaremos redis para melhorá-lo.
@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) {
// 发送前先看下我们是否已经缓存了验证码
String yzm = redisTemplate.opsForValue().get("yzm");
// 判断是否存在
if (yzm == null){
// 生成六位数验证码
int authNum = new Random().nextInt(899999) + 100000;
String authCode = String.valueOf(authNum);
// 不存在,我们发送邮箱给用户
SendMailUtil.sendEmailCode(targetEmail, "你的验证码为:" + authCode + "(五分钟内有效)");
// 存入redis中,设置有效期为1分钟
redisTemplate.opsForValue().set("yzm", authCode, 1, TimeUnit.MINUTES);
return "发送成功";
}
// 存在,直接返回,不再发送邮箱~
return "请勿重复发送验证码";
}
}
Depois de testar novamente dessa maneira, pode-se descobrir que o clique maluco não tem mais efeito e foi interceptado com sucesso, o que é muito mais seguro
Neste ponto, o efeito que queríamos no início foi realizado na pequena demonstração, e então podemos apresentar nosso próprio projeto oficial
4. Problemas de implantação online
De acordo com o código acima, ele roda normalmente localmente, mas se for implantado no ambiente online, ocorre o seguinte erro:
1.Sending the email to the following server failed : smtp.163.com:465
2.Could not connect to SMTP host: smtp.163.com, port: 465
3.No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
Razão: Fabricantes de servidores, como Alibaba Cloud, desabilitaram a porta padrão 25. Precisamos usar as portas disponíveis, como 465, para enviar e-mails e habilitar conexões SSL, e realizar as seguintes configurações relacionadas. Finalmente, abra a janela correspondente no firewall do servidor.
/**
* 验证获取操作安全证书
*/
public class CheckCodeUtils {
/**
* 发送邮件代码
*
* @param targetEmail 目标用户邮箱
* @param authCode 发送的验证码
*/
public static String GetEmailCode(String targetEmail, String authCode) {
try {
// 创建邮箱对象
SimpleEmail mail = new SimpleEmail();
// 设置发送邮件的服务器
mail.setHostName("smtp.qq.com");
// "你的邮箱号"+ "上文开启SMTP获得的授权码"
mail.setAuthentication("[email protected]", "GHNUxxxxxVL");
// 发送邮件 "你的邮箱号"+"发送时用的昵称"
mail.setFrom("[email protected]", "伙伴匹配系统");
// 发送服务端口
mail.setSslSmtpPort(String.valueOf(465));
// 使用安全链接
mail.setSSLOnConnect(true);
System.setProperty("mail.smtp.ssl.enable", "true");
System.setProperty("mail.smtp.ssl.protocols", "TLSv1.2");
// 接收用户的邮箱
mail.addTo(targetEmail);
// 邮件的主题(标题)
mail.setSubject("注册验证码");
// 邮件的内容
mail.setMsg("【伙伴匹配系统】您的验证码为:" + authCode + "(5分钟内有效)");
// 发送
mail.send();
return "发送成功,请注意查收";
} catch (EmailException e) {
return e.getMessage();
}
}
}
Cinco. Front-end (suplemento)
Eu simplesmente escrevi uma interface com js nativo, você pode dar uma olhada se estiver interessado
código mostra como abaixo:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<input id="mail" type="text">
<button id="getCode">获取验证码</button>
</div>
<script>
/*按钮禁用60秒,并显示倒计时*/
function disabledButton() {
const getCode = document.querySelector("#getCode")
getCode.disabled = true
let second = 60;
const intervalObj = setInterval(function () {
getCode.innerText = "请" + second + "秒后再重试"
if (second === 0) {
getCode.innerText = "获取验证码"
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>