.Net 6 implements rotation verification code

The previous articles introduced the sliding verification code function implemented by .Net 6. Recently, the sliding verification code has been ImageSharpreplaced by the sliding verification code SkiaSharp. The cutout part refers to the code of pojianbing. After sliding the verification code, I thought to myself. Make a rotating captcha. In fact, the rotating verification code is similar to the sliding verification code.

Let’s take a look at the rendering first:
insert image description here
the implementation principle of rotation is the same as that of the sliding verification code:

  • 1. Get the background image
  • 2. Obtain the groove map and slider map of the template
  • 3. Carry out groove cutout
  • 4. Rotate the canvas
  • 5. Map to the slider map
  • 6. Overlay the groove map template on the background image

Captcha entity class ImageCaptchaInfo

public class ImageCaptchaInfo
{
    
    
    /// <summary>
    /// 背景图宽
    /// </summary>
    public int BackgroundImageWidth {
    
     get; set; }
    /// <summary>
    /// 背景图高
    /// </summary>
    public int BackgroundImageHeight {
    
     get; set; }
    /// <summary>
    /// 背景图
    /// </summary>
    public string BackgroundImageBase64 {
    
     get; set; }
    /// <summary>
    /// 滑动块图宽
    /// </summary>
    public int SliderImageWidth {
    
     get; set; }
    /// <summary>
    /// 滑动块图高
    /// </summary>
    public int SliderImageHeight {
    
     get; set; }
    /// <summary>
    /// 滑动块图
    /// </summary>
    public string SliderImageBase64 {
    
     get; set; }

    /// <summary>
    /// 随机值
    /// </summary>
    public int RandomX {
    
     get; set; }

    /// <summary>
    /// 容错值,可以为空!
    /// </summary>
    public float Tolerant {
    
     get; set; }
    /// <summary>
    /// 验证码类型
    /// </summary>
    public string CaptchaType {
    
     get; set; }

    public float Percentage
    {
    
    
        get
        {
    
    
            if (BackgroundImageWidth <= 0) return 0;
            return 1.0F * RandomX / BackgroundImageWidth;
        }
    }
}

Among them, the random value RandomX is to participate in Percentagethe calculation of the sliding ratio. PercentageThe role will be discussed later.

Define the rotation captcha entity RotateImageCaptchaInfo

public class RotateImageCaptchaInfo : ImageCaptchaInfo
{
    
    
    /// <summary>
    /// 旋转多少度
    /// </summary>
    public float Degree {
    
     get; set; }


    public void Check()
    {
    
    
        //校验
        if (this.Degree <= 0) throw new TaCaptchaException($"RotateImageCaptchaInfo数据异常: {
      
      nameof(Degree)}小于等于0");
        if (this.RandomX <= 0) throw new TaCaptchaException($"RotateImageCaptchaInfo数据异常: {
      
      nameof(RandomX)}小于等于0");
        if (this.BackgroundImageWidth <= 0) throw new TaCaptchaException($"RotateImageCaptchaInfo数据异常: {
      
      nameof(BackgroundImageWidth)}小于等于0");
        if (this.BackgroundImageHeight <= 0) throw new TaCaptchaException($"RotateImageCaptchaInfo数据异常: {
      
      nameof(BackgroundImageHeight)}小于等于0");
        if (this.SliderImageWidth <= 0) throw new TaCaptchaException($"RotateImageCaptchaInfo数据异常: {
      
      nameof(SliderImageWidth)}小于等于0");
        if (this.SliderImageHeight <= 0) throw new TaCaptchaException($"RotateImageCaptchaInfo数据异常: {
      
      nameof(SliderImageHeight)}小于等于0");
        if (string.IsNullOrWhiteSpace(this.BackgroundImageBase64)) throw new TaCaptchaException($"RotateImageCaptchaInfo数据异常: {
      
      nameof(BackgroundImageBase64)}为空");
        if (string.IsNullOrWhiteSpace(this.SliderImageBase64)) throw new TaCaptchaException($"RotateImageCaptchaInfo数据异常: {
      
      nameof(SliderImageBase64)}为空");
    }
}

CheckIn the verification method, if the degree of rotation Degree, random value RandomX, etc. do not meet the conditions, an error will be returned automatically.

Define interface IRotateCaptchaImageGenerator

Define the rotation verification code interface, which is relatively simple, just a method to generate verification codes.

public interface IRotateCaptchaImageGenerator
{
    
    
	Task<RotateImageCaptchaInfo> Generate(string captchaId);
}

generate verification code

public class RotateCaptchaImageGenerator : IRotateCaptchaImageGenerator
{
    
    
    private readonly IResourceManager _resourceManager;
    private readonly Random _random = new Random();
      
    public RotateCaptchaImageGenerator(IResourceManager resourceManager)
    {
    
    
        _resourceManager = resourceManager;
            
    }

    public async Task<RotateImageCaptchaInfo> Generate(string captchaId)
    {
    
    

        //获取背景图
        var background = await _resourceManager.RandomBackground();
        //获取滑块图、凹槽图
        (var slider, var notch) = await _resourceManager.RandomTemplate(CaptchaTypeConstant.ROTATE);

        using var backgroundImage = SKBitmap.Decode(background);
        //滑块
        using var sliderTemplateImage = SKBitmap.Decode(slider);
        //凹槽
        using var notchTemplateImage = SKBitmap.Decode(notch);

        //定义空的凹槽图与滑块图
        using var notchMattingImage = new SKBitmap(notchTemplateImage.Width, notchTemplateImage.Height);
        using var sliderBarImage = new SKBitmap(360,360);


        //根据透明度计算凹槽图轮廓形状(形状由不透明区域形成)
        var notchShape = CaptchaImageUtils.GetImageShape(notchTemplateImage);

        //滑块图画布
        using var sliderCanvas = new SKCanvas(notchMattingImage);
        //叠加轮廓图
        sliderCanvas.ClipPath(notchShape, SKClipOperation.Intersect, true);
        //计算居中的位置
        int x = backgroundImage.Width / 2 - notchTemplateImage.Width / 2;
        int y = backgroundImage.Height / 2 - notchTemplateImage.Height / 2;
        //随机旋转抠图部分

        int randomX = _random.Next(notchTemplateImage.Width + 10, backgroundImage.Width - 10);
        float degree = 360f - randomX / (backgroundImage.Width / 360f);
        sliderCanvas.RotateDegrees(degree, notchMattingImage.Width/2, notchMattingImage.Height/2);

        //绘制抠图
        sliderCanvas.DrawBitmap(backgroundImage,-x,-y);
        //叠加滑块模板
        sliderCanvas.DrawBitmap(sliderTemplateImage, 0, 0);
            

        int bw = 360;
        int bh = 360;
        int cw = notchMattingImage.Width;
        int ch = notchMattingImage.Height;
        int iw = (int)(bw / 2 - cw / 2);
        int ih = (int)(bh / 2 - ch / 2);

        using var sliderBarCanvas = new SKCanvas(sliderBarImage);
        sliderBarCanvas.DrawBitmap(notchMattingImage, iw, ih);
           


           

        using var backgroundCanvas = new SKCanvas(backgroundImage);
        //叠加凹槽
        backgroundCanvas.DrawBitmap(notchTemplateImage, x, y);

        return new RotateImageCaptchaInfo
        {
    
    
            BackgroundImageWidth = backgroundImage.Width,
            BackgroundImageHeight = backgroundImage.Height,
            BackgroundImageBase64 = backgroundImage.ToBase64String(SKEncodedImageFormat.Png),
            RandomX = randomX,
            Degree = degree,
            Tolerant = 0.03f,
            SliderImageWidth = notchMattingImage.Width,
            SliderImageHeight = notchMattingImage.Height,
            SliderImageBase64 = sliderBarImage.ToBase64String(SKEncodedImageFormat.Png),
            CaptchaType = CaptchaTypeConstant.ROTATE
        };

    }
}

In the code, first get the background image, groove image and slider image, and convert them to SKBitmap.

Define an empty groove and slider plot. According to the groove map, get the path of the groove map.

Get Groove Cutout

/// <summary>
/// 获取图形轮廓,形状由不透明的区域形成
/// </summary>
/// <param name="image">要获取轮廓的图片</param>
/// <returns></returns>
public static SKPath GetImageShape(SKBitmap image)
{
    
    
    int temp = 0;
    var path = new SKPath();
    for(int y=0;y< image.Height;y++)
    {
    
    
        for(int x=0;x< image.Width;x++)
        {
    
    
            var pixel = image.GetPixel(x, y);
            if(pixel.Alpha!=0)
            {
    
    
                if(temp==0)
                {
    
    
                    temp = x;
                }
            }
            else
            {
    
    
                if(temp!=0)
                {
    
    
                    path.AddRect(new SKRect(temp, y, x, y + 1));
                    temp= 0;
                }
            }
        }
    }
    return path;
}

In the method, define a path , and paththen obtain the pixel information of each block column by column, and use the method to get one . There are 5 attributesSKBitmap GetPixelSKColorSKColor

  • Alpha
  • Blue
  • Green
  • Hue
  • Among
    them, we only need to judge that the judgment Alphais not 0, that is, it is not transparent.

After obtaining the groove cutout, you need to define one SKCanvas, which I call a canvas. The name is sliderCanvas, sliderCanvasthe background cutout needs to be drawn on top.
The method of use ClipPathfirst sliderCanvassuperimposes the contour map notchShape, that is, the groove matting we just generated. ClipPathThe method has multiple overloads, and the parameters used here are: SKPath, SKClipOperationand antialias.
The first parameter is the contour image just obtained notchShape, the second parameter is selected SKClipOperation.Intersectto take the intersection of two areas, and the third parameter is set to true, which means anti-aliasing.
After superimposing the contour map, first calculate the position to be deducted on the background map. First get the center position of the background image and subtract the center position of the groove image. The obtained x and y values ​​are the coordinates of the starting point to be drawn.
Then according to the random value, randomly rotate the canvas, using RotateDegreesthe method. RotateDegreesThe first parameter of the method is the degree of rotation, and the second and third parameters determine the center point of the rotation.
It is written to draw the background cutout part on the canvas, and draw the slider map template on the canvas.
Then define a canvas for the slider sliderBarCanvas. Draw the cutout made on the slider.
Finally, draw the groove map onto the background map.
After everything is drawn, return the information to the front end, and that's it. SKBitmapThe base64 string returned in the code is an extension I wrote myself , which is very simple

public static string ToBase64String(this SKBitmap source, SKEncodedImageFormat format)
{
    
    
    using var img = SKImage.FromBitmap(source);
    using SKData data = img.Encode(format, 100);
    var array = data.ToArray();
    return "data:" + format.ToString().ToLower() + ";base64," + Convert.ToBase64String(array, 0, array.Length);
}

so far. The code for rotating captcha is all done.

front-end code

Like the previous sliding verification code, there are very few places that need to be modified. resetIn the method, set the position of the slider to the position of the center point, and set the rotation to 0

$(".captcha_slider").css('left', (captchaMainWidth - captchaMainHeight)/2);
$(".captcha_slider").css("transform", "rotate(0deg)")

handleDragMovingmethod, set the slider to selected instead of moving from left to right

$('#sliderSrc').css('transform',"rotate("+(_x/(206/360))+"deg)");

Others are unchanged. The front-end code is introduced in detail in this article
. NET 6 realizes the sliding verification code (10), the finale: front-end code combat, vue3 and HTML+JQuery

Summarize

There are other types of verification codes, such as click verification codes, graphic verification codes, etc. It will gradually be perfected and realized.

Click on the official account card below to follow me! Learn together and progress together!

Guess you like

Origin blog.csdn.net/sd2208464/article/details/128507887