发送手机/邮箱验证码

时间 2017/9/18 10:45:11 加载中...

场景

在用户注册,密码更改时,经常需要用户填写验证码,我们一般会把验证码发送给用户手机,或者用户邮箱。用户收到验证码后,再填写进表单。在用户提交表单时,我们会验证验证码的正确性和时效性。这里看一下具体实现。

代码实现(C#,MySql)

   1.实现向手机/邮箱发送验证码  
   2.实现验证手机和验证码或者邮箱和验证码的正确性和时效性

1. 建立验证码表

首先创建一个验证码表来存储发送的验证码,这里使用是MySql数据库。

drop table if exists ValidationCode;

create table ValidationCode
(
   ValidationCodeID     int not null COMMENT '验证码唯一标识',
   Status               int COMMENT '状态:0删除,1未删除',
   CreateUser           int COMMENT '创建人id',
   CreateTime           datetime COMMENT '创建时间',
   ModifyUser           int COMMENT '修改人id',
   ModifyTime           datetime COMMENT '修改时间',
   Tel		        nvarchar(20) COMMENT '手机',
   Email		nvarchar(50) COMMENT '邮箱',
   Code                 int COMMENT '验证码',
   primary key (ValidationCodeID)
);
alter table ValidationCode comment '验证码表';

注意,添加注释很重要,也推荐纳入项目规范。其中里面的“创建人id”“修改人id”不是很重要,有些情况下还不会有值。

2. 添加对应的Model

新建一个名为ValidationCode的类。  

public class ValidationCode 
{
        public int ValidationCodeID { get; set; }
        public int Status { get; set; }
        public int? CreateUser { get; set; }
        public DateTime CreateTime { get; set; }
        public int? ModifyUser { get; set; }
        public DateTime ModifyTime { get; set; }
        
        public string Tel { get; set; }
        public string Email { get; set; } 
        public string Code { get; set; }           
}


3. 添加数据库访问类ValidationCodeDAO

在数据库访问类中包含的方法有:

1. void Add(ValidationCode model) //新增
2. void Save(ValidationCode model)//保存
2. ValidationCode GetByEmail(string email) //通过邮箱获取记录
3. ValidationCode GetByTel(string tel) //通过手机号获取记录

具体代码如下:

public class ValidationCodeDAO
{
    public void Add(ValidationCode model)
    {
        context.Insert(model);
    }

    public void Save(ValidationCode model)
    {
        context.Update(model);
    }

    public ValidationCode GetByEmail(string email)
    {
        string sql = string.Format("select * from ValidationCode where status = 1 and email = '{0}'", email);
        var models = context.ExecuteSql(sql);
        return models.FirstOrDefault();
    }

    public ValidationCode GetByTel(string tel)
    {
        string sql = string.Format("select * from ValidationCode where status = 1 and tel = '{0}'", tel);
        var models = context.ExecuteSql(sql);
        return models.FirstOrDefault();
    }
}

上面代码中用到的Context是项目中封装好的上下文,因代码量过大,不便提供,可以考虑使用mysqlClient+Dapper来实现。

4. 添加服务接口IValidationCodeService

具体代码如下:

public interface IValidationCodeService
{
    //新增邮件验证码
    void AddEmailCode(string email, string code);
    //新增手机验证码
    void AddTelCode(string tel, string code);

    ValidationCode GetByEmail(string email);
    ValidationCode GetByTel(string tel);

    //校验邮件验证码
    CheckResult CheckEmail(string email, string code, int period = 15);
    //校验手机验证码
    CheckResult CheckTel(string tel, string code, int period = 15);
}

public enum CheckResult
{
    Ok,
    WrongCode,
    OverTime
}

5. 添加服务类ValidationCodeService

ValidationCodeService类实现了上面的接口类,具体代码如下:

public class ValidationCodeService : IValidationCodeService
{
    private IValidationCodeDAO validationCode;

    public ValidationCodeService(IValidationCodeDAO validationCode)
    {
        this.validationCode = validationCode;
    }

    public int Add(string email, string tel, string code)
    {
        var model = new ValidationCode();
        model.ValidationCodeID = KeyGenerator.GetNextKey("ValidationCode");
        model.CreateTime = DateTime.Now;
        model.ModifyTime = DateTime.Now;
        model.Status = 1;

        model.Email = email;
        model.Tel = tel;
        model.Code = code;

        this.validationCode.Add(model);

        return model.ValidationCodeID;
    }

    public void AddEmailCode(string email, string code)
    {
        ValidationCode model = validationCode.GetByEmail(email);
        if (model == null)
            this.Add(email, "", code);
        else
        {
            model.Code = code;
            model.ModifyTime = DateTime.Now;
            validationCode.Save(model);
        }
    }

    public void AddTelCode(string tel, string code)
    {
        ValidationCode model = validationCode.GetByTel(tel);
        if (model == null)
            this.Add("", tel, code);
        else
        {
            model.Code = code;
            model.ModifyTime = DateTime.Now;
            validationCode.Save(model);
        }
    }

    public ValidationCode GetByEmail(string email)
    {
        return this.validationCode.GetByEmail(email);
    }

    public ValidationCode GetByTel(string tel)
    {
        return this.validationCode.GetByTel(tel);
    }

    public void Save(ValidationCode model)
    {
        this.validationCode.Save(model);
    }

    public CheckResult CheckEmail(string email, string code, int period = 15)
    {
        ValidationCode model = validationCode.GetByEmail(email);
        if (model == null)
            return CheckResult.WrongCode;
        else if (model.Code != code)
            return CheckResult.WrongCode;
        else if (model.ModifyTime.AddMinutes(period) < DateTime.Now)
            return CheckResult.OverTime;
        else
            return CheckResult.Ok;
    }

    public CheckResult CheckTel(string tel, string code, int period = 15)
    {
        ValidationCode model = validationCode.GetByTel(tel);
        if (model == null)
            return CheckResult.WrongCode;
        else if (model.Code != code)
            return CheckResult.WrongCode;
        else if (model.ModifyTime.AddMinutes(period) < DateTime.Now)
            return CheckResult.OverTime;
        else
            return CheckResult.Ok;
    }
}

上面的 this.validationCode = validationCode; 赋值形式,使用了项目中的Autofac来注入的。另外注意 AddEmailCode 方法和 AddTelCode 方法均不是直接将记录保存到了数据库中,而是先判断数据库中是否已存在了记录,如果有,更新记录。

5. MVC中控制器调用Service

Service中的方法现在已经提供了:新增邮件验证码,新增手机验证码,校验邮件验证码,校验手机验证码。这样就可以在控制器中调用了。

[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult SendEmailCode(string email)
{
    if (string.IsNullOrEmpty(email))
        return Json(new { code = 401, msg = "邮箱不能为空" });
    try
    {
        string title = ConfigHelper.GetChangePwdEmailTitle();
        string body = ConfigHelper.GetChangePwdEmailBody();
        string code = new Random().Next(100000, 999999).ToString();

        validateCodeService.AddEmailCode(email, code);

        body = body.Replace("{code}", code);
        mailInfoService.AddMailInfo(new Web.Search.Model.MailInfo { ReceiverAddess = email, Title = title, Body = body });
        return Json(new { code = 200, msg = "ok" });
    }
    catch (Exception ex)
    {
        LogOpr.Error(ex.Message);
        return Json(new { code = 500 });
    }
}

SendEmailCode:从配置文件中获取邮件标题和内容,之后生成随机的验证码。先调用我们之前写好的服务保存在数据库中。之后调用发送邮件服务。

[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult SendTelCode(string tel)
{
    if (string.IsNullOrEmpty(tel))
        return Json(new { code = 401, msg = "手机不能为空" });
    try
    {
        string body = ConfigHelper.GetChangePwdTelBody();
        string code = new Random().Next(100000, 999999).ToString();

        validateCodeService.AddTelCode(tel, code);
        body = body.Replace("{code}", code);

        smsInfoService.AddSmsInfo(new SmsInfo { RecieveNum = tel, RecieveName = "", Msg = body });
        return Json(new { code = 200, msg = "ok" });
    }
    catch (Exception ex)
    {
        LogOpr.Error(ex.Message);
        return Json(new { code = 500 });
    }
}

SendTelCode:从配置文件中获取短信内容,之后生成随机的验证码。先调用我们之前写好的服务保存在数据库中。之后调用发送短信发送服务。

版权说明
作者:SQBER
文章来源:http://sqber.com/articles/tel-email-send-validation-code.html
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。