福州网页建站维护有哪些专门做行业分析的网站
2025/12/30 8:22:19 网站建设 项目流程
福州网页建站维护有哪些,专门做行业分析的网站,centos wordpress安装教程,简述网站制作方案和主要内容、验证码原理#xff1a;不只是“看得清”那么简单验证码实现的完整流程大致如下#xff1a;验证码生成#xff1a;当用户请求时#xff0c;服务器端会生成并像向用户发送一条暗含信息的数据。数据解构#xff1a;用户收到数据后会对其进行解构并获取可能的真实信息。此时…、验证码原理不只是“看得清”那么简单验证码实现的完整流程大致如下验证码生成当用户请求时服务器端会生成并像向用户发送一条暗含信息的数据。数据解构用户收到数据后会对其进行解构并获取可能的真实信息。此时在规定时间内真人可以轻松获取信息而脚本或程序无法完成。人机验证用户将信息发送给服务端进行验证进行人机验证包括原始信息验证和行为验证。验证码流程我们可以使用音频、视频、文本出题、图像等数据形式来承载隐藏的信息。其统一原则就是在真人可以快速识别出信息前提下尽可能增加验证难度对抗代码程序化识别以提高人机验证准确率。以图像验证码为例可以通过原始信息验证和行为验证两个方式提高人机验证的准确率。对抗OCR或图形识别提高机器程序对图像中字符文本或图形信息的提取难度行为验证多样化交互模式点击、滑动、分析用户的鼠标轨迹、点击模式、滑动速度等行为特征结合原始信息比对综合验证。下面我们主要从对抗OCR或图形识别的角度分享下图形验证码生成部分的实现内容主要为以下四部分图元绘制、干扰元素、形变滤镜、图形挖取。二、图元绘制项目基于SkiaSharp开发只需要去Nuget拉取组件SkiaSharp就可实现含文字图形渲染、编辑、编译GLSLOpenGL着色器语言创建shader等所需功能。本文使用的是3.119.1老版本传参方式在新版本能用但已被标记为obsolete本文中使用的方法均为3.x中的新版本方法。2.1 初始化项目开始时首先需要创建一个空白的bitmap和canvas用于图形绘制及存储。在创建后可以将canvas初始化成白色。这样我们就有了一个基础的bitmap和canvas对象供后续操作了。using var bitmap new SKBitmap(width, height);using var canvas new SKCanvas(bitmap);canvas.Clear(SKColors.White); // 白色背景2.2 图形绘制像Yandex网站的验证码是通过点击图形而不是识别文字来实现人机验证的。因此我们需要验证码工具具备简单图形绘制的能力。我们以画一个鸭子为示例, 其原则就是传入之前初始化的canvas再创建绘画板在canvas上绘制图形private static void DrawDuck(SKCanvas canvas){// 线条画笔using var stroke new SKPaint{Color SKColors.DarkRed,StrokeWidth 3,IsAntialias true,Style SKPaintStyle.Stroke};float cx canvas.LocalClipBounds.MidX,cy canvas.LocalClipBounds.MidY;// 1. 身体大圆弧当背胸canvas.DrawCircle(cx, cy, 35, stroke); // 胖身体// 2. 脑袋头顶小圆canvas.DrawCircle(cx 25, cy - 20, 20, stroke);// 3. 扁嘴上下两条短直线canvas.DrawLine(cx 45, cy - 20, cx 60, cy - 18, stroke);canvas.DrawLine(cx 45, cy - 20, cx 60, cy - 22, stroke);// 4. 小圆眼using var dot new SKPaint { Color SKColors.Black, IsAntialias true };canvas.DrawCircle(cx 30, cy - 25, 2.5f, dot);// 5. 尾巴一小撇canvas.DrawLine(cx - 35, cy - 5, cx - 45, cy 5, stroke);}预览生成的鸭子图像如下duck_pure2.2 字符绘制字符绘制是验证码的常见形式可以是数字符号也可以是中文。我们同样传入之前的canvas对象再创建绘画板并设定要绘制文字的字体以及位置。MeasureText可以估计文本的宽度font.Metrics可以估计文本基线到顶部距离这两个属性可以帮助我们定位文字。private static void DrawText(SKCanvas canvas, string text){using var textPaint new SKPaint{Color SKColors.DarkRed,IsAntialias true};var tf SKFontManager.Default.MatchFamily(Microsoft YaHei, SKFontStyle.Normal);using var font new SKFont(tf, canvas.LocalClipBounds.Height * 0.4f);var rand new Random();var clip canvas.LocalClipBounds;// 1. 先算总宽未旋转状态float totalWidth font.MeasureText(text, textPaint);float x clip.MidX - totalWidth / 2; // 整体水平居中起点float y clip.MidY font.Metrics.CapHeight / 2; // 计算文字从基线到顶部的距离canvas.DrawText(text, x, y, font, textPaint);return;}预览生成的文本图像如下萤火初芒现在我们已经能绘制核心元素了。可以通过系统随机选择图形或随机生成字符作为验证码的原始信息。但由于生成的图像过于简单了也很容易被OCR等程序直接读取并捕获因此我们需要进一步对验证码进行处理。后续案例均以文本验证码为例。三、干扰元素绘制在这里我们主要实现三类干扰元素干扰纹理、噪点、杂线直线和曲线。3.1 干扰纹理干扰纹理主要目的是对背景进行干扰通过生成随机的纹理来对抗OCR。同样的我们传入canvas对象后进行随机背景的绘制示例代码如下/// summary/// 在传入画布上铺满一层“电视雪花”噪点纹理并叠 3 条斜向扫描光斑最后以原尺寸绘制。/// /summary/// param namecanvas目标画布纹理将铺满其 LocalClipBounds 区域。/paramprivate static void CreateNoiseTexture(SKCanvas canvas){// 1. 取得画布当前可见区域整数尺寸var clip canvas.LocalClipBounds;int w (int)clip.Width;int h (int)clip.Height;// 2. 创建临时位图用于生成噪点using var bmp new SKBitmap(w, h, SKColorType.Rgba8888, SKAlphaType.Opaque);var rand new Random();/* 3. 逐像素写入随机灰度形成“雪花”噪点值域 230-255 保证噪点偏亮不会把背景压得太暗 */for (int y 0; y h; y){for (int x 0; x w; x){byte v (byte)rand.Next(230, 255); //控制背景纹理整体明暗度bmp.SetPixel(x, y, new SKColor(v, v, v));}}// 4. 生成 3 条斜向“扫描光斑”模拟老式 CRT 的反光条纹using var scanPaint new SKPaint{Color SKColors.White.WithAlpha(30), // 半透明白光Style SKPaintStyle.Fill,IsAntialias true};for (int i 0; i 3; i){// 每条光斑由 4 个顶点构成平行四边形宽度约 20 像素var path new SKPath();float y0 i * h / 3f;path.MoveTo(0, y0);path.LineTo(w, y0 80);path.LineTo(w, y0 100);path.LineTo(0, y0 20);path.Close();canvas.DrawPath(path, scanPaint);}// 5. 把刚才做好的噪点图一次性画到目标画布保持 1:1 像素对齐using var texturePaint new SKPaint { FilterQuality SKFilterQuality.None }; // 禁用插值保持锐利using var texture SKImage.FromBitmap(bmp);canvas.DrawImage(texture, 0, 0, texturePaint);}以上代码放在图像初始化背景之后执行。以下是纹理叠加文字验证码的效果萤火初芒_texture3.2 噪点和杂线同样的套路直接生成随机点和线即可// 画干扰线using (var linePaint new SKPaint{Color new SKColor(0, 0, 0, 90),StrokeWidth 1,IsAntialias true}){for (int i 0; i 6; i){var p1 new SKPoint(rnd.Next(width), rnd.Next(height));var p2 new SKPoint(rnd.Next(width), rnd.Next(height));canvas.DrawLine(p1, p2, linePaint);}// 或者用SKPath生成贝塞尔干扰线...}// 画噪点using (var pointPaint new SKPaint { Color new SKColor(0, 0, 0, 120) }){for (int i 0; i width * height / 150; i)canvas.DrawPoint(rnd.Next(width), rnd.Next(height), pointPaint);}效果预览萤火初芒_texture_pt_line四、干扰滤镜应用如果目前图像还是容易被识别为了对抗OCR我们要开始对原始图像进行形变了。这里尝试的方法主要有文字旋转整体波纹扭曲。4.1 文字随机旋转独立绘制每个文字并按随机角度生成private static void DrawText(SKCanvas canvas, string text){using var textPaint new SKPaint{Color SKColors.DarkRed,IsAntialias true};var tf SKFontManager.Default.MatchFamily(Microsoft YaHei, SKFontStyle.Normal);using var font new SKFont(tf, canvas.LocalClipBounds.Height * 0.4f);var rand new Random();var clip canvas.LocalClipBounds;// 1. 先算总宽未旋转状态float totalWidth font.MeasureText(text, textPaint);float x clip.MidX - totalWidth / 2; // 整体水平居中起点float y clip.MidY font.Metrics.CapHeight / 2;// 独立绘制每个字符foreach (var c in text){float fontWidth font.MeasureText(c.ToString(), textPaint);// 2. 每次保存当前画布状态canvas.Save();// 3. 以字符基线中心为原点旋转canvas.Translate(x fontWidth / 2, y);canvas.RotateDegrees(rand.NextSingle() * 30 - 15); // ±15°canvas.Translate(-fontWidth / 2, 0);// 4. 画单个字符canvas.DrawText(c.ToString(), 0, 0, font, textPaint);// 5. 恢复画布不影响下一个字canvas.Restore();x fontWidth; // 前进到下一个字的位置}}预览如下萤火初芒_texture_pt_line_ang4.2 波纹扭曲正弦波扭曲整个图像,这里直接通过glsl创造一个shader实现具体代码和详细注释如下public static SKBitmap WaveTortion(SKBitmap src,SKPoint center,float waveLength 30,float amplitude 12){/**************************************************************** 第 1 步把 CPU 里的 SKBitmap 包装成 GPU 可用的纹理采样器* 参数 2、3 是“越界采样模式”* Clamp 表示“边缘拉伸”避免边缘出现重复采样***************************************************************/using var texture SKShader.CreateBitmap(src,SKShaderTileMode.Clamp,SKShaderTileMode.Clamp);/**************************************************************** 第 2 步写一段 GLSL 片段着色器告诉 GPU 每个像素怎么算* 语法是 Skia 的 RuntimeEffect 方言和 OpenGL ES 2.0 几乎一样* 逐行解释写在注释里注意字符串里不能用 //)***************************************************************/const string glsl /* 0. Skia 规定入口函数必须是 half4 main(vec2 coord)coord 当前像素的“画布坐标”(像素单位) */uniform shader texture; /* 1. 声明一张纹理采样器名字随意 */uniform vec2 center; /* 2. 波纹中心由 C# 传进来 */uniform float waveLength;/* 3. 波长 λ */uniform float amplitude; /* 4. 振幅 A */half4 main(vec2 coord){/* 5. 计算当前像素到中心的向量 */vec2 dt coord - center;/* 6. 求径向距离 r √(dx²dy²) */float r length(dt);/* 7. 波纹偏移量正弦函数sin(r / λ * 2π) 保证一个完整周期长度正好是 λ 像素再乘以振幅 A单位变成“像素” */float offset sin(r / waveLength * 6.2831853) * amplitude;/* 8. 求单位方向向量避免 r0 时除 0 */vec2 dir (r 0.0) ? dt / r /* 单位化 */: vec2(0); /* 中心点直接给 0 *//* 9. 把当前像素坐标沿着径向推拉得到“采样坐标” */vec2 uv coord dir * offset;/* 10. 用新坐标去纹理里采样返回颜色 */return texture.eval(uv);};/**************************************************************** 第 3 步编译 GLSL* 如果写错语法Skia 会把错误字符串塞到 out 参数 err***************************************************************/using var effect SKRuntimeEffect.CreateShader(glsl, out var err);if (effect null)throw new Exception($GLSL 编译失败{err});/**************************************************************** 第 4 步把 C# 变量映射到 GLSL 的 uniform* 注意数组类型必须和 GLSL 声明完全一致* vec2 → float[2] float → float***************************************************************/var uniforms new SKRuntimeEffectUniforms(effect){[center] new[] { center.X, center.Y },[waveLength] waveLength,[amplitude] amplitude};/* 还要把“纹理采样器”绑定到 uniform shader */var children new SKRuntimeEffectChildren(effect){[texture] texture};/**************************************************************** 第 5 步创建一块空画布大小和原图一致* 然后整张贴一个矩形用刚才的 Shader 来“刷”颜色***************************************************************/var info new SKImageInfo(src.Width, src.Height);using var surface SKSurface.Create(info); // 离屏 GPU 画布using var paint new SKPaint(); // 画笔paint.Shader effect.ToShader(uniforms, children); // 关键把特效当笔刷surface.Canvas.DrawRect(info.Rect, paint); // 画满整张画布surface.Canvas.Flush(); // 确保命令立即提交/**************************************************************** 第 6 步把 GPU 里的结果读回 CPU生成新位图* FromImage 会拷贝一份调用方可安全 Dispose 原 surface***************************************************************/return SKBitmap.FromImage(surface.Snapshot());}其中波长waveLength决定了图像的扭曲密集程度取值越小扭曲越密集振幅amplitude决定了扭曲的剧烈程度不同的组合取值效果示意如下waveLength20,amplitude3.5萤火初芒_all_20_3.5waveLength40,amplitude7萤火初芒_all_40_7六、挖孔挖孔可应用与和用户行为结合的场景下即拉动水平滚动条使局部图像与挖孔位置对其。主要思路是复制一个bitmap通过设定BlendMode混合模式实现单独绘制孔洞的形状和孔洞外的形状。为了示意我们把孔和洞分开了真实场景下二者应该是同时出现的具体代码及效果如下/// summary/// 从源画布中心截取一个半径为 radius 的圆返回一张新的 SKBitmap。/// /summary/// param namesourceCanvas源画布仅用于获取尺寸和截取时的中心参考。/param/// param nameradius圆半径像素。/param/// returns仅包含圆形区域的透明背景位图。/returnsprivate static SKBitmap CutCircle(SKBitmap snapshot, int radius 10, bool keepCircle true){if (keepCircle false){var circleBmp new SKBitmap(snapshot.Width, snapshot.Height, SKColorType.Rgba8888, SKAlphaType.Premul);using var canvas new SKCanvas(circleBmp);// 1. 先画一个实心圆作为 Sourceusing var circlePaint new SKPaint { IsAntialias true, Color SKColors.White };canvas.DrawCircle(snapshot.Width / 2, snapshot.Height / 2, radius, circlePaint);// 2. 再用 SrcIn 把原图叠上去只保留与圆重叠的部分using var imgPaint new SKPaint { BlendMode SKBlendMode.SrcOut };var srcRect new SKRect(0, 0, snapshot.Width, snapshot.Height);canvas.DrawBitmap(snapshot, srcRect, imgPaint);return circleBmp;}else{var circleBmp new SKBitmap(radius * 2, radius * 2, SKColorType.Rgba8888, SKAlphaType.Premul);using var canvas new SKCanvas(circleBmp);// 1. 先画一个实心圆作为 Sourceusing var circlePaint new SKPaint { IsAntialias true, Color SKColors.White };canvas.DrawCircle(radius, radius, radius, circlePaint);// 2. 再用 SrcIn 把原图叠上去只保留与圆重叠的部分using var imgPaint new SKPaint { BlendMode SKBlendMode.SrcIn };var srcRect new SKRect(0, 0, snapshot.Width, snapshot.Height);var dstRect new SKRect(-(snapshot.Width / 2 - radius),-(snapshot.Height / 2 - radius),-(snapshot.Width / 2 - radius) snapshot.Width,-(snapshot.Height / 2 - radius) snapshot.Height);canvas.DrawBitmap(snapshot, srcRect, dstRect, imgPaint);return circleBmp;}}

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询