以前の記事では、.Net 6 によって実装されたスライド認証コード機能を紹介しました。最近、スライド認証コードはスライド認証コードにImageSharp
置き換えられSkiaSharp
、切り抜き部分は pojianbing のコードを指します。確認コードをスライドさせた後、私は考えました。回転キャプチャを作成します。実際、ローテーション認証コードはスライド認証コードに似ています。
最初にレンダリングを見てみましょう:
回転の実装原理は、スライド検証コードの原理と同じです:
- 1.背景画像を取得する
- 2. テンプレートのグルーブマップとスライダーマップを取得する
- 3. 溝切りを行う
- 4.キャンバスを回転する
- 5. スライダー マップへのマッピング
- 6.背景画像にグルーヴマップのテンプレートを重ねる
目次
キャプチャ エンティティ クラス 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;
}
}
}
その中で、ランダム値は、スライド率の計算RandomX
に参加することです。役割については後述します。Percentage
Percentage
ローテーション キャプチャ エンティティを定義します 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)}为空");
}
}
Check
検証方法では、回転度Degree
やランダム値RandomX
などが条件を満たさない場合、自動的にエラーを返します。
インターフェイス IRotateCaptchaImageGenerator を定義する
ローテーション検証コード インターフェイスを定義します。これは比較的単純で、検証コードを生成するためのメソッドです。
public interface IRotateCaptchaImageGenerator
{
Task<RotateImageCaptchaInfo> Generate(string captchaId);
}
確認コードを生成する
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
};
}
}
コードでは、最初に背景画像、溝画像、スライダー画像を取得し、それらを に変換しますSKBitmap
。
空の溝とスライダー プロットを定義します。グルーブ マップに従って、グルーブ マップのパスを取得します。
溝のカットアウトを取得
/// <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;
}
メソッドでパスを定義し、path
各ブロックのピクセル情報を列ごとに取得し、メソッドを使用して を取得します。属性は5つSKBitmap
GetPixel
SKColor
SKColor
- アルファ
- 青
- 緑
- フエ
- その中で
、判定がAlpha
0ではない、つまり透けていないと判断すればいいだけです。
SKCanvas
溝のカットアウトを取得したら、キャンバスと呼ぶものを定義する必要があります。名前は ですsliderCanvas
。sliderCanvas
背景の切り抜きを上に描画する必要があります。
使用方法は、ClipPath
最初にsliderCanvas
コンター マップnotchShape
、つまり生成したばかりの溝マットを重ね合わせます。ClipPath
メソッドには複数のオーバーロードがあり、ここで使用されるパラメーターは 、SKPath
およびSKClipOperation
ですantialias
。
最初のパラメーターは取得したばかりの輪郭画像notchShape
、2 番目のパラメーターはSKClipOperation.Intersect
2 つの領域の交点を取得するように選択され、3 番目のパラメーターは true に設定されます。これはアンチエイリアシングを意味します。
等高線図を重ね合わせたら、まず背景図上で差し引く位置を計算します。まず背景画像の中心位置を取得し、溝画像の中心位置を差し引きます。得られた x と y の値は、描画する始点の座標です。
次に、ランダム値に従って、RotateDegrees
メソッドを使用してキャンバスをランダムに回転させます。RotateDegrees
メソッドの最初のパラメーターは回転の角度で、2 番目と 3 番目のパラメーターは回転の中心点を決定します。
背景の切り抜き部分をキャンバスに描画し、スライダーマップのテンプレートをキャンバスに描画するように書かれています。
次に、スライダーのキャンバスを定義しますsliderBarCanvas
。スライダーに作った切り抜きを描きます。
最後に、バックグラウンド マップにグルーブ マップを描画します。
すべてが描画されたら、情報をフロントエンドに返します。それだけです。コードで返される base64 文字列は、私が自分で作成したSKBitmap
拡張機能であり、非常に単純です。
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);
}
ここのところ。キャプチャを回転させるためのコードはすべて完了です。
フロントエンドコード
先ほどのスライド検証コードと同様に、修正が必要な箇所は非常に少ないですが、reset
メソッドではスライダーの位置を中心点の位置に設定し、回転を0に設定します
$(".captcha_slider").css('left', (captchaMainWidth - captchaMainHeight)/2);
$(".captcha_slider").css("transform", "rotate(0deg)")
handleDragMoving
メソッドでは、スライダーを左から右に移動する代わりに選択済みに設定します
$('#sliderSrc').css('transform',"rotate("+(_x/(206/360))+"deg)");
その他は変更ありません。フロントエンドコードはこちらの記事で詳しく紹介しています
. NET 6で実現するスライド検証コード (10), フィナーレ: フロントエンドコードの戦闘, vue3とHTML+JQuery
要約する
クリック検証コード、グラフィック検証コードなど、他の種類の検証コードがあります。それは徐々に完成され、実現されます。
下の公式アカウントカードをクリックしてフォローしてください!一緒に学び、一緒に進歩しましょう!