个人技术总结 --- 基于SpringBoot使用Email进行验证码校验

一、技术概述

​ 做什么:基于SpringBoot使用Email进行验证码校验方法;原因:有关注册、忘记密码功能需要一定的验证来保证账号的安全性;难点:分为两部分,一部分就是如何发送email邮件,另一部分就是如何校验验证码,下面也将从这两个难点出发进行阐述。

二、技术详述

首先介绍以下如何发送email邮件:

​ 1)准备工作

  • 需要开启POP3、SMTP邮件服务
  • 需要设置客户端授权码
  • 以网易邮箱举例如下:

2)、发送邮件模板

i、添加依赖

在Spring Boot的工程中的 pom.xml 中引入 spring-boot-starter-mail 依赖:

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

ii、绑定配置

application.yml

spring:
    mail:
        host: smtp.163.com
        username: xxxxxx@163.com
        password: xxxxxxxxxxxxxx
        properties:
            mail:
                smtp:
                    starttls:
                        enable: true
                        required: true
注意:在spring.mail.password处的值是需要在邮箱设置里面生成的授权码,这个不是真实的密码。

iii、测试发送

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public class ApplicationTests {

    @Autowired
    private JavaMailSender mailSender;

    @Test
    public void sendSimpleMail() throws Exception {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom("dyc87112@qq.com");	//设置邮件发送者
        message.setTo("10001@qq.com");		//设置邮件接收者
        message.setSubject("主题:简单邮件");	//设置邮件主题
        message.setText("测试邮件内容");		//设置邮件内容

        mailSender.send(message);
    }

}

引入了 spring-boot-starter-mail 依赖之后,会根据配置文件中的内容去创建 JavaMailSender 实例,因此我们可以直接在需要使用的地方直接 @Autowired 来引入邮件发送对象。

到此,一个简单的邮件发送就完成,也就说我们已经能够将生成的随机验证码发送到用户邮箱,那么如何根据生成的验证码进行校验呢?也就是上面提到的难点的另一部分


思路也很简单:就是将随机生成的验证码correctCode加密后的密文hash和时间tamp放入map中一并返回给前端,校验时,前端再传回的map和用户输入的验证码inputCode,再将inputCode进行同样的加密与传回的map中的hash进行对比,相同则验证码输入正确,否则输入错误,如此也能设置tamp保证一定的时效性。

上代码:

private static final String KEY = "XXXX"; // KEY为自定义秘钥

原文采用依赖Google的guava包中的MD5加密,我比较懒,不想导入依赖,直接用Spring框架中继承的DigestUtils工具类进行加密。

加密:

String code = VerifyCode(6);	//随机数生成6位验证码
sendEmail(code);				//发送验证码
SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddHHmmss");
Calendar c = Calendar.getInstance();
c.add(Calendar.MINUTE, 5);		
String currentTime = sf.format(c.getTime());// 生成5分钟后时间,用户校验是否过期

String hash =  DigestUtils.md5DigestAsHex((KEY + "@" + currentTime + "@" + code).getBytes());//生成MD5值
Map<String, Object> resultMap = new HashMap<>();	//返回的map
resultMap.put("hash", hash);
resultMap.put("tamp", currentTime);
return resultMap;

校验:

public String VerifyCode(@RequestBody Map<String,Object> requestMap, @PathVariable("verification") String code){
    String requestHash = requestMap.get("hash").toString();
    String tamp = requestMap.get("tamp").toString();
    SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddHHmmss");//当前时间
    Calendar c = Calendar.getInstance();
    String currentTime = sf.format(c.getTime());
    if (tamp.compareTo(currentTime) > 0) {
        String hash =  DigestUtils.md5DigestAsHex((KEY + "@" + tamp + "@" + code).getBytes());//生成MD5值
        if (hash.equalsIgnoreCase(requestHash)){
            //校验成功
            return "success";
        }else {
            //验证码不正确,校验失败
            return "failed";
        }
    } else {
        // 超时
        return "failed";
    }
}

到此,就完成验证码校验而不必每次都存储新生成的验证码等待验证而浪费存储空间。安全性与采用的加解密算法相关,应该也相对安全。

三、技术使用中遇到的问题和解决过程。

困难:除了上述两个难点(主要问题)的解决以外(在参考了别人的思路之后,可以说已经没什么大问题了),只剩其中一些琐碎的小问题,比如验证码如何生成:

由于对验证码要求不高,所以就直接采用生成n位数字(更复杂的可以是字母数字组合,或者与图形结合)验证码生成比较简单,只是通过随机数生成n位数字,采用随机数即可解决。

生成验证码:

private String VerifyCode(int n){
    Random r = new Random();
    StringBuffer sb =new StringBuffer();
    for(int i = 0;i < n;i ++){
        int ran1 = r.nextInt(10);
        sb.append(String.valueOf(ran1));
    }
//        System.out.println(sb);
    return sb.toString();
}

还有一个小问题便是如何对生成验证码进行加密,这里在查阅资料后了解到Springboot已经封装了相应的工具类,不用白不用嘛,即如下DigestUtils 的 md5加密方法,这里我往里添加了KEY我们的默认密钥,currenttime用来保证验证码的时效性,上面代码已经提到,这里不再赘述。

String hash =  DigestUtils.md5DigestAsHex((KEY + "@" + currentTime + "@" + code).getBytes());//生成MD5值

值得一提的是,还有一点,就是上述配置在部署时可能出错,因为如果不设置端口的话,email默认端口port将会是25,而在阿里云服务器上25 port好像是被禁用的,因此我们最好改一下端口配置,否则在部署到云端时发送email功能不能正常运行,配置如下:

spring:
    mail:
        host: smtp.163.com
        username: a_shadouyou@163.com
        password: OKQXJTQRTZGZACRK
        properties:
            mail:
                smtp:
                    starttls:
                        enable: true
                        required: true
                    socketFactory:
                        class: javax.net.ssl.SSLSocketFactory
                        port: 465	#改端口为465,云端服务器只需开启465端口即可

到此,已经解决了当初的需求,完成了邮箱验证的功能。

四、总结。

上面比较繁杂,说了大多是细节,这里进行一些简略的小结:

完成邮箱验证需要由两部分:生成验证码发送到用户邮箱 - -> 对用户的输入验证码进行校验。

发送邮件简单,通过设置邮箱后,直接导入依赖,完成相应配置(这里是application.yml ,properties也可),然后编写相应业务逻辑即可;

校验验证码比较复杂,但思路也很简单,就是将随机生成的验证码加密后的密文返回给前端,校验时,前端再传回的map和用户根据邮箱提示输入的验证码,前端页面再将输入验证码与一开始收到的密文传回,后端进行校验,相同则验证码输入正确,否则输入错误。

说白了,就是这么简单,这都归功于Spring Boot 封装的工具类以及结合的依赖给我们的开发带来了极大的便利啊!

五、参考

参考如下:

SpringBoot发送注册激活邮件(附HTML模板)(作者:不爱编程的设计师)

SpringBoot配置Email发送功能(作者:牧野流冰87)

SpringBoot实现短信验证码校验(作者:一只因特马)

注:以上参考可能不是来自第一手作者。

posted @ 2020-06-15 19:47  _Spike  阅读(2556)  评论(4编辑  收藏  举报