上一篇文章介绍如何根据背景图片、模板的滑块和凹槽生成验证码。这篇内容我们来实现调用生成方法,并加入验证码的验证功能。
上一篇内容地址:.NET 6 实现滑动验证码(七)、生成验证码
验证码验证
在Validator目录下,新建BaseValidator.cs
using SlideCaptcha.Interface;
using SlideCaptcha.Model;
using System;
namespace SlideCaptcha.Validator
{
public abstract class BaseValidator : IValidator
{
public bool Validate(SlideTrack slideTrack, CaptchaValidateData captchaValidateData)
{
if (slideTrack == null) throw new ArgumentNullException(nameof(slideTrack));
if (captchaValidateData == null) throw new ArgumentNullException(nameof(captchaValidateData));
slideTrack.Check();
var min = captchaValidateData.Percent - captchaValidateData.Tolerant;
var max = captchaValidateData.Percent + captchaValidateData.Tolerant;
if (slideTrack.Percent < min || slideTrack.Percent > max)
{
return false;
}
return ValidateCore(slideTrack, captchaValidateData);
}
public abstract bool ValidateCore(SlideTrack slideTrack, CaptchaValidateData captchaValidateData);
}
}
Validate 方法接受两个参数,slideTrack为前端传递过来的数据,captchaValidateData为验证码生成时的数据。先通过默认的滑动比例进行判断,如果通过了。调用ValidateCore方法进行进一步验证;
这里ValidateCore是一个抽象类,需要进一步实现。
新建BasicValidator.cs,用来实现BaseValidator中定义的抽象类
using SlideCaptcha.Interface;
using SlideCaptcha.Model;
namespace SlideCaptcha.Validator
{
public class BasicValidator : BaseValidator, IValidator
{
public override bool ValidateCore(SlideTrack slideTrack, CaptchaValidateData captchaValidateData)
{
slideTrack.CheckTracks();
// 进行行为轨迹检测
var startSlidingTime = slideTrack.StartTime.ToFileTimeUtc();
long endSlidingTime = slideTrack.EndTime.ToFileTimeUtc();
var bgImageWidth = slideTrack.BackgroundImageWidth;
var trackList = slideTrack.Tracks;
// check1: 滑动时间如果小于300毫秒 返回false
if (startSlidingTime + 300 > endSlidingTime)
{
return false;
}
// check2:轨迹数据要是少于10,或者大于背景宽度的五倍 返回false
if (trackList.Count < 10 || trackList.Count > bgImageWidth * 5)
{
return false;
}
// check3:x轴和y轴应该是从0开始的,要是一开始x轴和y轴乱跑,返回false
var firstTrack = trackList[0];
if (firstTrack.X > 10 || firstTrack.X < -10 || firstTrack.Y > 10 || firstTrack.Y < -10)
{
return false;
}
// check4: 如果y轴是相同的,必然是机器操作,直接返回false (暂时去掉,容易失败)
// check5:x轴或者y轴直接的区间跳跃过大的话返回 false (暂时去掉,容易失败)
// check6: 如果x轴超过图片宽度的频率过高,返回false
int check4 = 1;
int check6 = 0;
for (int i = 1; i < trackList.Count; i++)
{
var track = trackList[i];
// check4
if (firstTrack.Y == track.Y) check4++;
// check7
if (track.X >= bgImageWidth) check6++;
// check5
var preTrack = trackList[i - 1];
if ((track.X - preTrack.X) > 50 || (track.Y - preTrack.Y) > 50) return false; // 快速来回拖动可能导致这里验证不通过
}
if (check4 == trackList.Count || check6 > 200)
{
return false;
}
//return true;
// check7: x轴应该是由快到慢的, 要是速率一致,返回false
int splitPos = (int)(trackList.Count * 0.7);
var splitPostTrack = trackList[splitPos - 1];
int posTime = splitPostTrack.T;
float startAvgPosTime = posTime / (float)splitPos;
var lastTrack = trackList[trackList.Count - 1];
float endAvgPosTime = (lastTrack.T - posTime) / (float)(trackList.Count - splitPos);
return endAvgPosTime > startAvgPosTime;
}
}
}
调用生成验证码方法
在根目录新建ImageCaptcha.cs,实现ICaptcha定义的生成验证码方法和验证方法。
using Microsoft.Extensions.Options;
using SlideCaptcha.Constant;
using SlideCaptcha.Interface;
using SlideCaptcha.Model;
using System;
using System.Threading.Tasks;
namespace SlideCaptcha
{
public class ImageCaptcha : ICaptcha
{
private readonly CaptchaOptions _options;
private ISliderCaptchaImageGenerator _sliderCaptchaImageGenerator;
private readonly IValidator _validator;
private readonly IStorage _storage;
public ImageCaptcha(
IValidator validator,
IStorage storage,
IOptionsSnapshot<CaptchaOptions> options,
ISliderCaptchaImageGenerator sliderCaptchaImageGenerator
)
{
_options = options.Value;
_storage = storage;
_validator = validator;
_sliderCaptchaImageGenerator = sliderCaptchaImageGenerator;
}
public async Task<CaptchaData> GenerateCaptchaImageAsync(string type, string captchaId = null)
{
captchaId = string.IsNullOrWhiteSpace(captchaId) ? Guid.NewGuid().ToString() : captchaId;
if(string.IsNullOrEmpty(type))
{
type = CaptchaTypeConstant.SLIDER;
}
if (type.Equals(CaptchaTypeConstant.SLIDER,StringComparison.OrdinalIgnoreCase))
{
var sliderImageCaptchaInfo = await _sliderCaptchaImageGenerator.Generate(captchaId);
sliderImageCaptchaInfo.Check();
var captchaValidateData = new CaptchaValidateData(sliderImageCaptchaInfo.Percent, _options.Tolerant);
_storage.Set(captchaId, captchaValidateData, DateTime.Now.AddSeconds(_options.ExpirySeconds).ToUniversalTime());
return new CaptchaData(captchaId, sliderImageCaptchaInfo.BackgroundImageBase64, sliderImageCaptchaInfo.SliderImageBase64);
}
return new CaptchaData(captchaId, "","");
}
public ValidateResult Validate(string captchaId, SlideTrack slideTrack)
{
try
{
var captchaValidateData = _storage.Get<CaptchaValidateData>(captchaId);
if (captchaValidateData == null) return ValidateResult.Timeout();
var success = _validator.Validate(slideTrack, captchaValidateData);
return success ? ValidateResult.Success() : ValidateResult.Fail();
}
finally
{
_storage.Remove(captchaId);
}
}
}
}
在项目根目录下新建一个 ValidateResult.cs,作用是定义验证码验证的结果。
namespace SlideCaptcha
{
public class ValidateResult
{
public ValidateResultType Result { get; set; }
public string Message { get; set; }
/// <summary>
/// 验证成功
/// </summary>
/// <returns></returns>
public static ValidateResult Success()
{
return new ValidateResult { Result = ValidateResultType.Success, Message = "成功" };
}
/// <summary>
/// 验证失败
/// </summary>
/// <returns></returns>
public static ValidateResult Fail()
{
return new ValidateResult { Result = ValidateResultType.ValidateFail, Message = "验证失败" };
}
/// <summary>
/// 验证超时
/// </summary>
/// <returns></returns>
public static ValidateResult Timeout()
{
return new ValidateResult { Result = ValidateResultType.Timeout, Message = "验证超时" };
}
public enum ValidateResultType
{
Success = 0,
ValidateFail = 1,
Timeout = 2
}
}
}
代码定义了3种状态,验证成功、验证失败和超时。
至此,所有代码全部完成了。可以使用打包功能打成nupkg包,也可以在项目中进行引用。
下一篇,介绍如何创建一个验证码API服务端。
下载方式:
点击下方公众号卡片,关注我,回复captcha
免费领取!