.NET 6 实现滑动验证码(八)、调用生成方法,行为验证

上一篇文章介绍如何根据背景图片、模板的滑块和凹槽生成验证码。这篇内容我们来实现调用生成方法,并加入验证码的验证功能。

上一篇内容地址:.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 免费领取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倾斜的水瓶座

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值