目录
本文介绍如何后端实现图形验证码,包括纯数字验证码、数字+字母验证码以及数字运算验证码
1.关于ZKWeb.System.Drawing
这是一个与.Net核心兼容的系统。绘图实现从mono项目修改而来。适用于windows和linux。
适用系统:
- Windows 8.1 64bit
- Ubuntu Server 16.04 LTS 64bit
- Fedora 24 64bit
- CentOS 7.2 64bit
实现的功能:
- 打开jpg, bmp, ico, png格式的图片
- 保存 jpg, bmp, ico, png 格式的图片
- 调整图片大小
- 绘制图形
- 打开字体并绘制字符串
2.绘制图形验证码
验证码信息实体类对象
/// <summary>
/// 验证码信息
/// </summary>
public class VerifyCode
{
/// <summary>
/// 验证码
/// </summary>
public string Code { get; set; }
/// <summary>
/// 验证码数据流
/// </summary>
public byte[] Image { get; set; }
/// <summary>
/// base64
/// </summary>
public string Base64Str { get { return Convert.ToBase64String(Image); } }
}
验证码类型枚举信息
/// <summary>
/// 验证码类型
/// </summary>
public enum VerifyCodeType
{
[Description("纯数字验证码")]
NUM=0,
[Description("数字加字母验证码")]
CHAR=1,
[Description("数字运算验证码")]
ARITH =2,
}
生成数字验证码
/// <summary>
/// 获取数字验证码
/// </summary>
/// <param name="n">验证码数</param>
/// <returns></returns>
public static string CreateNumCode(int n)
{
char[] numChar = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
string charCode = string.Empty;
Random random = new Random();
for (int i = 0; i < n; i++)
{
charCode += numChar[random.Next(numChar.Length)];
}
return charCode;
}
生成数字+字母验证码
/// <summary>
/// 获取字符验证码
/// </summary>
/// <param name="n">验证码数</param>
/// <returns></returns>
public static string CreateCharCode(int n)
{
char[] strChar = { 'a', 'b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3',
'4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K',
'L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
string charCode = string.Empty;
Random random = new Random();
for (int i = 0; i < n; i++)
{
charCode += strChar[random.Next(strChar.Length)];
}
return charCode;
}
生成数字运算验证码
/// <summary>
/// 获取运算符验证码
/// </summary>
/// <returns></returns>
public static string CreateArithCode(out string resultCode)
{
string checkCode ="";
Random random = new Random();
int intFirst = random.Next(1, 20);//生成第一个数字
int intSec = random.Next(1, 20);//生成第二个数字
int intTemp = 0;
switch (random.Next(1, 3).ToString())
{
case "2":
if (intFirst < intSec)
{
intTemp = intFirst;
intFirst = intSec;
intSec = intTemp;
}
checkCode = intFirst + "-" + intSec + "=";
resultCode = (intFirst - intSec).ToString();
break;
default:
checkCode = intFirst + "+" + intSec + "=";
resultCode = (intFirst + intSec).ToString();
break;
}
return checkCode;
}
绘制验证码图形
/// <summary>
/// 获取验证码
/// </summary>
/// <param name="n">验证码数</param>
/// <param name="type">类型 0:数字 1:字符</param>
/// <returns></returns>
public static VerifyCode CreateVerifyCode(int n, VerifyCodeType type)
{
int codeW = 170;//宽度
int codeH = 50;//高度
int fontSize = 32;//字体大小
//初始化验证码
string charCode = string.Empty;
string resultCode = "";
switch (type.ToString())
{
case "NUM":
charCode = CreateNumCode(n);
break;
case "ARITH":
charCode = CreateArithCode(out resultCode);
n = charCode.Length;
break;
default:
charCode = CreateCharCode(n);
break;
}
//颜色列表
Color[] colors = { Color.Black, Color.Red, Color.Blue, Color.Green, Color.Orange, Color.Brown, Color.DarkBlue };
//字体列表
string[] fonts = { "Times New Roman", "Verdana", "Arial", "Gungsuh" };
//创建画布
Bitmap bitmap = new Bitmap(codeW, codeH);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White);
Random random = new Random();
//画躁线
for (int i = 0; i < n; i++)
{
int x1 = random.Next(codeW);
int y1 = random.Next(codeH);
int x2 = random.Next(codeW);
int y2 = random.Next(codeH);
Color color = colors[random.Next(colors.Length)];
Pen pen = new Pen(color);
graphics.DrawLine(pen, x1, y1, x2, y2);
}
//画噪点
for (int i = 0; i < 100; i++)
{
int x = random.Next(codeW);
int y = random.Next(codeH);
Color color = colors[random.Next(colors.Length)];
bitmap.SetPixel(x, y, color);
}
//画验证码
for (int i = 0; i < n; i++)
{
string fontStr = fonts[random.Next(fonts.Length)];
Font font = new Font(fontStr, fontSize);
Color color = colors[random.Next(colors.Length)];
graphics.DrawString(charCode[i].ToString(), font, new SolidBrush(color), (float)i * 30 + 5, (float)0);
}
//写入内存流
try
{
MemoryStream stream = new MemoryStream();
bitmap.Save(stream, ImageFormat.Jpeg);
VerifyCode verifyCode = new VerifyCode()
{
Code = type.ToString()== "ARITH" ? resultCode :charCode,
Image = stream.ToArray()
};
return verifyCode;
}
//释放资源
finally
{
graphics.Dispose();
bitmap.Dispose();
}
}
3.Api接口获取验证码及用户登录验证码验证
验证码获取接口示例
/// <summary>
/// 生成图片验证码
/// </summary>
/// <param name="codeType">验证码类型 0:纯数字 1:数字+字母 2:数字运算 默认1</param>
/// <returns></returns>
[AllowAnonymous]
[HttpGet("captcha")]
[Log(Title = "生成图片验证码")]
public ApiResult Captcha(string codeType="1")
{
string uuid = Guid.NewGuid().ToString().Replace("-", "");
var codeInfo = new CommonMethod.VerifyCode();
if (codeType == "1")
{
codeInfo = CommonMethod.VerifyCodeHelper.CreateVerifyCode(4, CommonMethod.VerifyCodeType.CHAR);
}
else if (codeType == "2")
{
codeInfo = CommonMethod.VerifyCodeHelper.CreateVerifyCode(4, CommonMethod.VerifyCodeType.ARITH);
}
else if (codeType == "0")
{
codeInfo = CommonMethod.VerifyCodeHelper.CreateVerifyCode(4, CommonMethod.VerifyCodeType.NUM);
}
CacheHelper.SetCache(uuid, codeInfo.Code);
var obj = new { uuid, img = codeInfo.Base64Str };
return ToJson(1, obj);
}
用户登录验证码验证接口示例
[Route("login")]
[HttpPost]
[Log(Title = "登录")]
public IActionResult Login([FromBody] LoginBodyDto loginBody)
{
if (loginBody == null) { throw new CustomException("请求参数错误"); }
loginBody.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext);
if (CacheHelper.Get(loginBody.Uuid) is string str && !str.ToLower().Equals(loginBody.Code.ToLower()))
{
return ToResponse(ResultCode.CAPTCHA_ERROR, "验证码错误");
}
CacheHelper.Remove(loginBody.Uuid);//移除验证码
SysLogininfor sysLogininfor = RecordLogInfo(httpContextAccessor.HttpContext);
if (string.IsNullOrEmpty(sysLogininfor.CreateBy))
{
sysLogininfor.userName = loginBody.Username;
sysLogininfor.CreateBy = loginBody.Username;
sysLogininfor.CreateName = loginBody.Username;
}
var user = sysLoginService.Login(loginBody, sysLogininfor);
List<SysRole> roles = roleService.SelectUserRoleListByUserId(user.ID);
//权限集合 eg *:*:*,system:user:list
List<string> permissions = permissionService.GetMenuPermission(user);
LoginUser loginUser = new(user, roles, permissions);
CacheService.SetUserPerms(GlobalConstant.UserPermKEY + user.ID, permissions);
return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser), jwtSettings.JwtSettings));
}
效果展示