diff --git a/Lazy.Captcha.Core/CaptchaOptions.cs b/Lazy.Captcha.Core/CaptchaOptions.cs
index fc7816a505793a86bbf670a156a4ec816aed349a..3cbc1a88116c3d8865944c9f65583a423ae79d1f 100644
--- a/Lazy.Captcha.Core/CaptchaOptions.cs
+++ b/Lazy.Captcha.Core/CaptchaOptions.cs
@@ -1,12 +1,5 @@
using Lazy.Captcha.Core.Generator;
-using Lazy.Captcha.Core.Generator.Code;
-using Lazy.Captcha.Core.Generator.Image;
using Lazy.Captcha.Core.Generator.Image.Option;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace Lazy.Captcha.Core
{
@@ -18,7 +11,7 @@ namespace Lazy.Captcha.Core
///
/// 验证码类型
///
- public CaptchaType CaptchaType
+ public virtual CaptchaType CaptchaType
{
get { return _captchaType; }
set
@@ -41,28 +34,28 @@ namespace Lazy.Captcha.Core
}
///
- /// 验证码长度 当CaptchaType为ARITHMETIC, ARITHMETIC_ZH时, 长度代表乘数个数
+ /// 验证码长度
///
- public int CodeLength { get; set; } = DEFAULT_CODE_LENGTH;
+ public virtual int CodeLength { get; set; } = DEFAULT_CODE_LENGTH;
///
/// 过期时长
///
- public int ExpirySeconds { get; set; } = 60;
+ public virtual int ExpirySeconds { get; set; } = 60;
///
/// code比较是否忽略大小写
///
- public bool IgnoreCase { get; set; } = true;
+ public virtual bool IgnoreCase { get; set; } = true;
///
/// 存储键前缀
///
- public string StoreageKeyPrefix { get; set; }
+ public virtual string StoreageKeyPrefix { get; set; }
///
/// 图片选项
///
- public CaptchaImageGeneratorOption ImageOption { get; set; } = new CaptchaImageGeneratorOption();
+ public virtual CaptchaImageGeneratorOption ImageOption { get; set; } = new CaptchaImageGeneratorOption();
}
}
\ No newline at end of file
diff --git a/Lazy.Captcha.Core/CaptchaService.cs b/Lazy.Captcha.Core/CaptchaService.cs
index c404ac1c1db3ee5d2c274d859c10cd938e65163a..d32769d3874d4df862bd5b6a9245f48337fe5481 100644
--- a/Lazy.Captcha.Core/CaptchaService.cs
+++ b/Lazy.Captcha.Core/CaptchaService.cs
@@ -1,18 +1,14 @@
-using Lazy.Captcha.Core.Generator.Code;
+using System;
+using Lazy.Captcha.Core.Generator.Code;
using Lazy.Captcha.Core.Generator.Image;
using Lazy.Captcha.Core.Storage;
-using Microsoft.Extensions.Caching.Memory;
-using Microsoft.Extensions.Options;
-using System;
-using System.Collections.Generic;
-using System.Text;
namespace Lazy.Captcha.Core
{
public class CaptchaService
{
- private CaptchaOptions CaptchaOptions;
- private IStorage Storage;
+ private readonly CaptchaOptions CaptchaOptions;
+ private readonly IStorage Storage;
///
/// 构造函数
@@ -24,16 +20,18 @@ namespace Lazy.Captcha.Core
Storage = storage;
}
+
+
///
/// 生成验证码
///
/// 验证码id
/// 缓存时间,未设定则使用配置时间
///
- public CaptchaData Generate(string captchaId, int? expirySeconds = null)
+ public virtual CaptchaData Generate(string captchaId, int? expirySeconds = null)
{
- var _captchaCodeGenerator = new DefaultCaptchaCodeGenerator(CaptchaOptions.CaptchaType);
- var _captchaImageGenerator = new DefaultCaptchaImageGenerator();
+ var _captchaCodeGenerator = GetCodeGenerator();
+ var _captchaImageGenerator = GetImmageGenerator();
var (renderText, code) = _captchaCodeGenerator.Generate(CaptchaOptions.CodeLength);
var image = _captchaImageGenerator.Generate(renderText, CaptchaOptions.ImageOption);
@@ -43,6 +41,16 @@ namespace Lazy.Captcha.Core
return new CaptchaData(captchaId, code, image);
}
+ protected virtual ICaptchaImageGenerator GetImmageGenerator()
+ {
+ return new DefaultCaptchaImageGenerator();
+ }
+
+ protected virtual ICaptchaCodeGenerator GetCodeGenerator()
+ {
+ return new DefaultCaptchaCodeGenerator(CaptchaOptions.CaptchaType);
+ }
+
///
/// 校验
///
@@ -50,7 +58,7 @@ namespace Lazy.Captcha.Core
/// 用户输入的验证码
/// 校验成功时是否移除缓存(用于多次验证)
///
- public bool Validate(string captchaId, string code, bool removeIfSuccess = true, bool removeIfFail = true)
+ public virtual bool Validate(string captchaId, string code, bool removeIfSuccess = true, bool removeIfFail = true)
{
var val = Storage.Get(captchaId);
var comparisonType = CaptchaOptions.IgnoreCase ? StringComparison.CurrentCultureIgnoreCase : StringComparison.CurrentCulture;
diff --git a/Lazy.Captcha.Core/EvaluationEngine.cs b/Lazy.Captcha.Core/EvaluationEngine.cs
deleted file mode 100644
index 1815d6256f03254de2e818911c940f343af8e436..0000000000000000000000000000000000000000
--- a/Lazy.Captcha.Core/EvaluationEngine.cs
+++ /dev/null
@@ -1,199 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace Lazy.Captcha.Core
-{
- public class EvaluationEngine
- {
- ///
- /// 操作符级别映射
- ///
- private Dictionary OperatorLevelMaps = new Dictionary
- {
- { '(', 1 },
- { ')', 1 },
- { '+', 2 },
- { '-', 2 },
- { 'x', 3 },
- { '*', 3 },
- { '÷', 3 },
- { '/', 3 },
- { '^', 4 },
- { '√', 4 },
- { '%', 4 }
- };
-
- ///
- /// 是否数字
- ///
- ///
- ///
- private bool IsNumber(string str)
- {
- return double.TryParse(str, out var _);
- }
-
- ///
- /// 是否数字
- ///
- ///
- ///
- private bool IsNumber(char c)
- {
- var charCode = (int)c;
- return charCode >= 48 && charCode <= 57;
- }
-
- ///
- /// 转为逆波兰
- ///
- ///
- ///
- private List ConvertToRPN(string exp)
- {
- var output = new List();
- var index = 0;
- var operatorStack = new Stack();
-
- while (index < exp.Count())
- {
- var currentIndex = index;
- var prevIndex = index - 1;
- var prev = prevIndex < 0 ? "" : exp[prevIndex] + "";
- var current = exp[currentIndex];
- var isNegativeSign = current == '-' && currentIndex == 0;
-
- if (OperatorLevelMaps.ContainsKey(current) && !isNegativeSign)
- {
- if (current == '(')
- {
- operatorStack.Push(current);
- }
- else if (current == ')')
- {
- while (operatorStack.Peek() != '(')
- {
- output.Add(operatorStack.Pop().ToString());
- }
- operatorStack.Pop(); // )弹出
- }
- else
- {
- while (operatorStack.Count != 0 && operatorStack.Peek() != '(' && OperatorLevelMaps[current] <= OperatorLevelMaps[operatorStack.Peek()])
- {
- output.Add(operatorStack.Pop().ToString());
- }
- operatorStack.Push(current);
- }
-
- index++;
- }
- else
- {
- if (isNegativeSign) index++;
- // 扫描连续数字: -1.23
- while (index < exp.Count() && (IsNumber(exp[index]) || exp[index] == '.') && exp[index] != ' ')
- {
- index++;
- }
- output.Add(exp.Substring(currentIndex, index - currentIndex));
- }
- }
-
- while (operatorStack.Count != 0) output.Add(operatorStack.Pop().ToString());
-
- return output;
- }
-
- ///
- /// 根据运算符计算
- ///
- ///
- ///
- ///
- ///
- ///
- private double Cacl(double v1, double v2, string op)
- {
- switch (op)
- {
- case "+":
- return v1 + v2;
- case "-":
- return v1 - v2;
- case "x":
- case "*":
- return v1 * v2;
- case "÷":
- case "/":
- return v1 / v2;
- case "^":
- return Math.Pow(v1, v2);
- }
-
- throw new Exception("未知操作符");
- }
-
- ///
- /// 根据逆波兰计算
- ///
- ///
- ///
- private double CalcRPN(List rpn)
- {
- var stack = new Stack();
-
- for (var i = 0; i < rpn.Count; i++)
- {
- var item = rpn[i];
-
- if (IsNumber(item))
- {
- stack.Push(item);
- }
- else
- {
- if (item == "√")
- {
- var v = stack.Pop();
- stack.Push((Math.Sqrt((double)v)).ToString());
- }
- else if (item == "%")
- {
- stack.Push((PopDouble() / 100.0d).ToString());
- }
- else
- {
- var v1 = PopDouble();
- var v2 = PopDouble();
- var v = Cacl(v2, v1, item);
- stack.Push(v.ToString());
- }
- }
-
- double PopDouble()
- {
- var v = (string)stack.Pop();
- return double.Parse(v);
- }
- }
-
- return double.Parse((string)stack.Pop());
- }
-
- ///
- /// 计算
- ///
- ///
- ///
- public double Evaluate(string exp)
- {
- exp = exp.Replace(" ", "");
- var rpn = ConvertToRPN(exp);
- return CalcRPN(rpn);
- }
- }
-}
diff --git a/Lazy.Captcha.Core/Generator/Code/Characters.cs b/Lazy.Captcha.Core/Generator/Code/Characters.cs
index 2fab895a4a9d8f94668ef0f3b23e3112a383a6bc..2315e1546304a6a11d9003ee022af6d262f19555 100644
--- a/Lazy.Captcha.Core/Generator/Code/Characters.cs
+++ b/Lazy.Captcha.Core/Generator/Code/Characters.cs
@@ -1,98 +1,59 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace Lazy.Captcha.Core.Generator.Code
{
- public class Characters
+ public static class Characters
{
///
/// 小写英文字符
///
- public static List WORD_LOWER = new List { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
+ public static IReadOnlyList WORD_LOWER { get; } = new ReadOnlyCollection(new[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' });
///
/// 大写英文字符
///
- public static List WORD_UPPER = new List { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
+ public static IReadOnlyList WORD_UPPER { get; } = new ReadOnlyCollection(new[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' });
///
/// 中文
///
- public static List CHINESE = new List { '的', '一', '国', '在', '人', '了', '有', '中', '是', '年', '和', '大', '业', '不', '为', '发', '会', '工', '经', '上', '地', '市', '要', '个', '产', '这', '出', '行', '作', '生', '家', '以', '成', '到', '日', '民', '来', '我', '部', '对', '进', '多', '全', '建', '他', '公', '开', '们', '场', '展', '时', '理', '新', '方', '主', '企', '资', '实', '学', '报', '制', '政', '济', '用', '同', '于', '法', '高', '长', '现', '本', '月', '定', '化', '加', '动', '合', '品', '重', '关', '机', '分', '力', '自', '外', '者', '区', '能', '设', '后', '就', '等', '体', '下', '万', '元', '社', '过', '前', '面', '农', '也', '得', '与', '说', '之', '员', '而', '务', '利', '电', '文', '事', '可', '种', '总', '改', '三', '各', '好', '金', '第', '司', '其', '从', '平', '代', '当', '天', '水', '省', '提', '商', '十', '管', '内', '小', '技', '位', '目', '起', '海', '所', '立', '已', '通', '入', '量', '子', '问', '度', '北', '保', '心', '还', '科', '委', '都', '术', '使', '明', '着', '次', '将', '增', '基', '名', '向', '门', '应', '里', '美', '由', '规', '今', '题', '记', '点', '计', '去', '强', '两', '些', '表', '系', '办', '教', '正', '条', '最', '达', '特', '革', '收', '二', '期', '并', '程', '厂', '如', '道', '际', '及', '西', '口', '京', '华', '任', '调', '性', '导', '组', '东', '路', '活', '广', '意', '比', '投', '决', '交', '统', '党', '南', '安', '此', '领', '结', '营', '项', '情', '解', '议', '义', '山', '先', '车', '然', '价', '放', '世', '间', '因', '共', '院', '步', '物', '界', '集', '把', '持', '无', '但', '城', '相', '书', '村', '求', '治', '取', '原', '处', '府', '研', '质', '信', '四', '运', '县', '军', '件', '育', '局', '干', '队', '团', '又', '造', '形', '级', '标', '联', '专', '少', '费', '效', '据', '手', '施', '权', '江', '近', '深', '更', '认', '果', '格', '几', '看', '没', '职', '服', '台', '式', '益', '想', '数', '单', '样', '只', '被', '亿', '老', '受', '优', '常', '销', '志', '战', '流', '很', '接', '乡', '头', '给', '至', '难', '观', '指', '创', '证', '织', '论', '别', '五', '协', '变', '风', '批', '见', '究', '支', '那', '查', '张', '精', '每', '林', '转', '划', '准', '做', '需', '传', '争', '税', '构', '具', '百', '或', '才', '积', '势', '举', '必', '型', '易', '视', '快', '李', '参', '回', '引', '镇', '首', '推', '思', '完', '消', '值', '该', '走', '装', '众', '责', '备', '州', '供', '包', '副', '极', '整', '确', '知', '贸', '己', '环', '话', '反', '身', '选', '亚', '么', '带', '采', '王', '策', '真', '女', '谈', '严', '斯', '况', '色', '打', '德', '告', '仅', '它', '气', '料', '神', '率', '识', '劳', '境', '源', '青', '护', '列', '兴', '许', '户', '马', '港', '则', '节', '款', '拉', '直', '案', '股', '光', '较', '河', '花', '根', '布', '线', '土', '克', '再', '群', '医', '清', '速', '律', '她', '族', '历', '非', '感', '占', '续', '师', '何', '影', '功', '负', '验', '望', '财', '类', '货', '约', '艺', '售', '连', '纪', '按', '讯', '史', '示', '象', '养', '获', '石', '食', '抓', '富', '模', '始', '住', '赛', '客', '越', '闻', '央', '席', '坚' };
+ public static IReadOnlyList CHINESE { get; } = new ReadOnlyCollection(new[] { '的', '一', '国', '在', '人', '了', '有', '中', '是', '年', '和', '大', '业', '不', '为', '发', '会', '工', '经', '上', '地', '市', '要', '个', '产', '这', '出', '行', '作', '生', '家', '以', '成', '到', '日', '民', '来', '我', '部', '对', '进', '多', '全', '建', '他', '公', '开', '们', '场', '展', '时', '理', '新', '方', '主', '企', '资', '实', '学', '报', '制', '政', '济', '用', '同', '于', '法', '高', '长', '现', '本', '月', '定', '化', '加', '动', '合', '品', '重', '关', '机', '分', '力', '自', '外', '者', '区', '能', '设', '后', '就', '等', '体', '下', '万', '元', '社', '过', '前', '面', '农', '也', '得', '与', '说', '之', '员', '而', '务', '利', '电', '文', '事', '可', '种', '总', '改', '三', '各', '好', '金', '第', '司', '其', '从', '平', '代', '当', '天', '水', '省', '提', '商', '十', '管', '内', '小', '技', '位', '目', '起', '海', '所', '立', '已', '通', '入', '量', '子', '问', '度', '北', '保', '心', '还', '科', '委', '都', '术', '使', '明', '着', '次', '将', '增', '基', '名', '向', '门', '应', '里', '美', '由', '规', '今', '题', '记', '点', '计', '去', '强', '两', '些', '表', '系', '办', '教', '正', '条', '最', '达', '特', '革', '收', '二', '期', '并', '程', '厂', '如', '道', '际', '及', '西', '口', '京', '华', '任', '调', '性', '导', '组', '东', '路', '活', '广', '意', '比', '投', '决', '交', '统', '党', '南', '安', '此', '领', '结', '营', '项', '情', '解', '议', '义', '山', '先', '车', '然', '价', '放', '世', '间', '因', '共', '院', '步', '物', '界', '集', '把', '持', '无', '但', '城', '相', '书', '村', '求', '治', '取', '原', '处', '府', '研', '质', '信', '四', '运', '县', '军', '件', '育', '局', '干', '队', '团', '又', '造', '形', '级', '标', '联', '专', '少', '费', '效', '据', '手', '施', '权', '江', '近', '深', '更', '认', '果', '格', '几', '看', '没', '职', '服', '台', '式', '益', '想', '数', '单', '样', '只', '被', '亿', '老', '受', '优', '常', '销', '志', '战', '流', '很', '接', '乡', '头', '给', '至', '难', '观', '指', '创', '证', '织', '论', '别', '五', '协', '变', '风', '批', '见', '究', '支', '那', '查', '张', '精', '每', '林', '转', '划', '准', '做', '需', '传', '争', '税', '构', '具', '百', '或', '才', '积', '势', '举', '必', '型', '易', '视', '快', '李', '参', '回', '引', '镇', '首', '推', '思', '完', '消', '值', '该', '走', '装', '众', '责', '备', '州', '供', '包', '副', '极', '整', '确', '知', '贸', '己', '环', '话', '反', '身', '选', '亚', '么', '带', '采', '王', '策', '真', '女', '谈', '严', '斯', '况', '色', '打', '德', '告', '仅', '它', '气', '料', '神', '率', '识', '劳', '境', '源', '青', '护', '列', '兴', '许', '户', '马', '港', '则', '节', '款', '拉', '直', '案', '股', '光', '较', '河', '花', '根', '布', '线', '土', '克', '再', '群', '医', '清', '速', '律', '她', '族', '历', '非', '感', '占', '续', '师', '何', '影', '功', '负', '验', '望', '财', '类', '货', '约', '艺', '售', '连', '纪', '按', '讯', '史', '示', '象', '养', '获', '石', '食', '抓', '富', '模', '始', '住', '赛', '客', '越', '闻', '央', '席', '坚' });
///
/// 数字
///
- public static List NUMBER = new List { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
+ public static IReadOnlyList NUMBER { get; } = new ReadOnlyCollection(new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' });
///
/// 中文数字小写
///
- public static List NUMBER_ZH_CN = new List { '零', '一', '二', '三', '四', '五', '六', '七', '八', '九' };
+ public static IReadOnlyList NUMBER_ZH_CN { get; } = new ReadOnlyCollection(new[] { '零', '一', '二', '三', '四', '五', '六', '七', '八', '九' });
///
/// 中文数字大写
///
- public static List NUMBER_ZH_HK = new List { '零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖' };
+ public static IReadOnlyList NUMBER_ZH_HK { get; } = new ReadOnlyCollection(new[] { '零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖' });
///
/// 英文字母大小写,数字混合
///
- public static List DEFAULT
- {
- get
- {
- var letters = new List();
- letters.AddRange(NUMBER);
- letters.AddRange(WORD_LOWER);
- letters.AddRange(WORD_UPPER);
- return letters;
- }
- }
+ public static IReadOnlyList DEFAULT { get; } = new ReadOnlyCollection(NUMBER.Concat(WORD_LOWER).Concat(WORD_UPPER).ToArray());
///
/// 英文字母大小写混合
///
- public static List WORD
- {
- get
- {
- var letters = new List();
- letters.AddRange(WORD_LOWER);
- letters.AddRange(WORD_UPPER);
- return letters;
- }
- }
+ public static IReadOnlyList WORD { get; } = new ReadOnlyCollection(WORD_LOWER.Concat(WORD_UPPER).ToArray());
///
/// 英文字母小写,数字混合
///
- public static List WORD_NUMBER_LOWER
- {
- get
- {
- var letters = new List();
- letters.AddRange(NUMBER);
- letters.AddRange(WORD_LOWER);
- return letters;
- }
- }
+ public static IReadOnlyList WORD_NUMBER_LOWER { get; } = new ReadOnlyCollection(NUMBER.Concat(WORD_LOWER).ToArray());
///
/// 英文字母大写,数字混合
///
- public static List WORD_NUMBER_UPPER
- {
- get
- {
- var letters = new List();
- letters.AddRange(NUMBER);
- letters.AddRange(WORD_UPPER);
- return letters;
- }
- }
+ public static IReadOnlyList WORD_NUMBER_UPPER { get; } = new ReadOnlyCollection(NUMBER.Concat(WORD_UPPER).ToArray());
}
}
\ No newline at end of file
diff --git a/Lazy.Captcha.Core/Generator/Code/DefaultCaptchaCodeGenerator.cs b/Lazy.Captcha.Core/Generator/Code/DefaultCaptchaCodeGenerator.cs
index a599a648492d31ed239eee95e57ba5f7cc9ae75a..b6939fdb87faa1634ee4ee351f687cd6c2dbd836 100644
--- a/Lazy.Captcha.Core/Generator/Code/DefaultCaptchaCodeGenerator.cs
+++ b/Lazy.Captcha.Core/Generator/Code/DefaultCaptchaCodeGenerator.cs
@@ -1,40 +1,45 @@
using System;
using System.Collections.Generic;
-using System.Linq;
+using System.Collections.ObjectModel;
using System.Text;
-using System.Threading.Tasks;
+using System.Threading;
namespace Lazy.Captcha.Core.Generator.Code
{
public class DefaultCaptchaCodeGenerator : ICaptchaCodeGenerator
{
- private CaptchaType _captchaType = CaptchaType.DEFAULT;
+ private readonly CaptchaType _captchaType = CaptchaType.DEFAULT;
- private static readonly Dictionary> TypeCharactersMap = new Dictionary>
+ private static IReadOnlyList GetCharacters(CaptchaType captchaType)
{
- { CaptchaType.DEFAULT , Characters.DEFAULT },
- { CaptchaType.CHINESE , Characters.CHINESE },
- { CaptchaType.NUMBER , Characters.NUMBER },
- { CaptchaType.NUMBER_ZH_CN , Characters.NUMBER_ZH_CN },
- { CaptchaType.NUMBER_ZH_HK , Characters.NUMBER_ZH_HK },
- { CaptchaType.WORD_NUMBER_LOWER , Characters.WORD_NUMBER_LOWER },
- { CaptchaType.WORD_NUMBER_UPPER , Characters.WORD_NUMBER_UPPER },
- { CaptchaType.WORD , Characters.WORD },
- { CaptchaType.WORD_LOWER , Characters.WORD_LOWER },
- { CaptchaType.WORD_UPPER , Characters.WORD_UPPER },
- };
-
- private static readonly Random random = new Random();
-
- private static readonly EvaluationEngine EvaluationEngine = new EvaluationEngine();
+ return captchaType switch
+ {
+ CaptchaType.DEFAULT => Characters.DEFAULT,
+ CaptchaType.CHINESE => Characters.CHINESE,
+ CaptchaType.NUMBER => Characters.NUMBER,
+ CaptchaType.NUMBER_ZH_CN => Characters.NUMBER_ZH_CN,
+ CaptchaType.NUMBER_ZH_HK => Characters.NUMBER_ZH_HK,
+ CaptchaType.WORD_NUMBER_LOWER => Characters.WORD_NUMBER_LOWER,
+ CaptchaType.WORD_NUMBER_UPPER => Characters.WORD_NUMBER_UPPER,
+ CaptchaType.WORD => Characters.WORD,
+ CaptchaType.WORD_LOWER => Characters.WORD_LOWER,
+ CaptchaType.WORD_UPPER => Characters.WORD_UPPER,
+
+ _ => Characters.DEFAULT,
+ };
+ }
+
+ private static readonly ThreadLocal ThreadRandom = new(() => new Random());
+ private static Random random => ThreadRandom.Value;
///
/// 中文操作符
///
- private static Dictionary OPERATOR_MAP = new Dictionary
- {
- { '+', '加' }, { '-', '减' }, { 'x', '乘' }
- };
+ private static IReadOnlyDictionary OPERATOR_MAP { get; } = new ReadOnlyDictionary(new Dictionary()
+ {
+ { '+', '加' }, { '-', '减' }
+ });
+
public DefaultCaptchaCodeGenerator() : this(CaptchaType.DEFAULT)
{
@@ -43,35 +48,36 @@ namespace Lazy.Captcha.Core.Generator.Code
public DefaultCaptchaCodeGenerator(CaptchaType captchaType)
{
- this._captchaType = captchaType;
+ _captchaType = captchaType;
}
///
/// 生成
///
- /// 长度(ARITHMETIC, ARITHMETIC_ZH 长度代表乘数个数)
+ /// 长度
/// (渲染文本,code)
public (string renderText, string code) Generate(int length)
{
- if (this._captchaType == CaptchaType.ARITHMETIC)
+ if (_captchaType == CaptchaType.ARITHMETIC)
{
return GenerateaArithmetic(length);
}
- else if (this._captchaType == CaptchaType.ARITHMETIC_ZH)
+ else if (_captchaType == CaptchaType.ARITHMETIC_ZH)
{
return GenerateaArithmeticZh(length);
}
- else if (this._captchaType == CaptchaType.NUMBER_ZH_CN)
+ else if (_captchaType == CaptchaType.NUMBER_ZH_CN)
{
return GenerateaNumberZH(length, false);
}
- else if (this._captchaType == CaptchaType.NUMBER_ZH_HK)
+ else if (_captchaType == CaptchaType.NUMBER_ZH_HK)
{
return GenerateaNumberZH(length, true);
}
else
{
- var code = Pick(TypeCharactersMap[this._captchaType], length);
+ var chars = GetCharacters(_captchaType);
+ var code = Pick(chars, length);
return (code, code);
}
}
@@ -82,7 +88,7 @@ namespace Lazy.Captcha.Core.Generator.Code
var sb2 = new StringBuilder();
var characters = isHk ? Characters.NUMBER_ZH_HK : Characters.NUMBER_ZH_CN;
- for (int i = 0; i < length; i++)
+ for (var i = 0; i < length; i++)
{
var num = random.Next(characters.Count);
sb1.Append(characters[num]);
@@ -95,43 +101,31 @@ namespace Lazy.Captcha.Core.Generator.Code
///
/// 生成算术表达式组成部分
///
- ///
+ /// 数字位数
///
- private (List operands, List operators) GenerateaArithmeticParts(int length)
+ private (int number1, char operators, int number2, int result) GenerateaArithmeticParts(int length)
{
- // 生成操作数
- var operands = new List();
- Enumerable.Range(0, length).ToList().ForEach(x => operands.Add(random.Next(10)));
-
- // 生成操作符
- var operators = new List();
- Enumerable.Range(0, length - 1).ToList().ForEach(x =>
+ var max = length switch
{
- switch (random.Next(3))
- {
- case 0:
- operators.Add('+');
- break;
- case 1:
- operators.Add('-');
- break;
- case 2:
- operators.Add('x');
- break;
- }
- });
-
- // fix: 在数字运算的时候出现减法,建议结果不要出现负数 https://gitee.com/pojianbing/lazy-captcha/issues/I6ATSJ
- // 多操作数比较复杂,目前仅修复两个操作数的情况
- if (length == 2 && operators[0] == '-')
- {
- var max = Math.Max(operands[0], operands[1]);
- var min = Math.Min(operands[0], operands[1]);
- operands[0] = max;
- operands[1] = min;
- }
-
- return (operands, operators);
+ 1 => 10,
+ 2 => 100,
+ 3 => 1000,
+ 4 => 10000,
+ 5 => 100000,
+ 6 => 1000000,
+ 7 => 10000000,
+ 8 => 100000000,
+ 9 => 1000000000,
+ _ => throw new ArgumentOutOfRangeException(nameof(length), "must 1 to 9")
+ };
+
+ var result = random.Next(max);
+ var number1 = random.Next(max);
+
+ if (number1 > result)
+ return (number1, '-', number1 - result, result);
+ else
+ return (number1, '+', result - number1, result);
}
///
@@ -141,19 +135,14 @@ namespace Lazy.Captcha.Core.Generator.Code
///
private (string renderText, string code) GenerateaArithmetic(int length)
{
- (var operands, var operators) = GenerateaArithmeticParts(length);
+ var (number1, operators, number2, result) = GenerateaArithmeticParts(length);
// 生成表达式
var sb = new StringBuilder();
- sb.Append(operands[0]);
- for (var i = 0; i < length - 1; i++)
- {
- sb.Append(operators[i]);
- sb.Append(operands[i + 1]);
- }
- var result = ((int)EvaluationEngine.Evaluate(sb.ToString())).ToString();
- return (sb.Append("=?").ToString(), result);
+ sb.Append(number1).Append(operators).Append(number2); //.Append("=?"); // 显示字符数量问题,不增加最后问号
+
+ return (sb.ToString(), result.ToString());
}
///
@@ -163,25 +152,19 @@ namespace Lazy.Captcha.Core.Generator.Code
///
private (string renderText, string code) GenerateaArithmeticZh(int length)
{
- (var operands, var operators) = GenerateaArithmeticParts(length);
-
- // 生成表达式
- var sb1 = new StringBuilder();
- var sb2 = new StringBuilder();
- sb1.Append(operands[0]);
- sb2.Append(Characters.NUMBER_ZH_CN[operands[0]]);
- for (var i = 0; i < length - 1; i++)
+ var (renderText, code) = GenerateaArithmetic(length);
+ var sb = new StringBuilder(renderText.Length);
+ foreach (var item in renderText)
{
- // 操作符
- sb1.Append(operators[i]);
- sb2.Append(OPERATOR_MAP[operators[i]]);
- // 操作符
- sb1.Append(operands[i + 1]);
- sb2.Append(Characters.NUMBER_ZH_CN[operands[i + 1]]);
+ if (item is >= '0' and <= '9')
+ sb.Append(Characters.NUMBER_ZH_CN[item - '0']);
+ else if (item is '+' or '-')
+ sb.Append(OPERATOR_MAP[item]);
+ else
+ sb.Append(item);
}
- var result = ((int)EvaluationEngine.Evaluate(sb1.ToString())).ToString();
- return (sb2.Append("=?").ToString(), result);
+ return (sb.ToString(), code);
}
///
@@ -190,11 +173,11 @@ namespace Lazy.Captcha.Core.Generator.Code
/// 字符列表
/// 数量
///
- private string Pick(List characters, int count)
+ private string Pick(IReadOnlyList characters, int count)
{
var result = new StringBuilder();
- for (int i = 0; i < count; i++)
+ for (var i = 0; i < count; i++)
{
result.Append(characters[random.Next(characters.Count)]);
}
diff --git a/Lazy.Captcha.Core/Generator/Image/DefaultCaptchaImageGenerator.cs b/Lazy.Captcha.Core/Generator/Image/DefaultCaptchaImageGenerator.cs
index cf284d659bb1606a307d67244bd76af4101b2b3f..bb57883956685e45a1c81b5976c4d058f28f3727 100644
--- a/Lazy.Captcha.Core/Generator/Image/DefaultCaptchaImageGenerator.cs
+++ b/Lazy.Captcha.Core/Generator/Image/DefaultCaptchaImageGenerator.cs
@@ -5,12 +5,8 @@ using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Drawing;
-using System.IO;
using System.Linq;
-using System.Net.NetworkInformation;
-using System.Text;
-using System.Threading.Tasks;
-
+using System.Threading;
namespace Lazy.Captcha.Core.Generator.Image
{
///
@@ -18,7 +14,8 @@ namespace Lazy.Captcha.Core.Generator.Image
///
public class DefaultCaptchaImageGenerator : ICaptchaImageGenerator
{
- private static Random Random = new Random();
+ private static readonly ThreadLocal ThreadRandom = new(() => new Random());
+ private static Random Random => ThreadRandom.Value;
///
/// 随机颜色
@@ -140,7 +137,7 @@ namespace Lazy.Captcha.Core.Generator.Image
for (var i = 0; i < count; i++)
{
- bool leftInBottom = Random.Next(2) == 0;
+ var leftInBottom = Random.Next(2) == 0;
int x1 = 5, y1 = leftInBottom ? Random.Next(height / 2, height - 5) : Random.Next(5, height / 2);
int x2 = width - 5, y2 = leftInBottom ? Random.Next(5, height / 2) : Random.Next(height / 2, height - 5);
int ctrlx1 = Random.Next(width / 4, width / 4 * 3), ctrly1 = Random.Next(5, height - 5);
@@ -175,7 +172,7 @@ namespace Lazy.Captcha.Core.Generator.Image
paint.Style = SKPaintStyle.Stroke;
paint.Color = gd.Color.WithAlpha((byte)(255 * gd.BlendPercentage));
- using (SKPath path = new SKPath())
+ using (var path = new SKPath())
{
path.MoveTo(gd.Start);
path.CubicTo(gd.Ctrl1, gd.Ctrl2, gd.End);
@@ -296,7 +293,7 @@ namespace Lazy.Captcha.Core.Generator.Image
var charXs = new List();
// 计算字体高度(取最高的)
- SKRect textBounds = new SKRect();
+ var textBounds = new SKRect();
paint.MeasureText(text, ref textBounds);
var fontHeight = (int)textBounds.Height;
@@ -307,7 +304,7 @@ namespace Lazy.Captcha.Core.Generator.Image
// 文字在包含框内的padding
var padding = (wrapperWidth - charWidths[i]) / 2;
var textX = currentX + padding;
- int textY = (height + fontHeight) / 2; // 文字的纵坐标
+ var textY = (height + fontHeight) / 2; // 文字的纵坐标
result.Add(new PointF(textX, textY));
currentX += wrapperWidth;
}
@@ -349,7 +346,7 @@ namespace Lazy.Captcha.Core.Generator.Image
// 绘制文字
DrawTexts(canvas, text, option);
- using (SKData p = bitmap.Encode(SKEncodedImageFormat.Jpeg, option.Quality))
+ using (var p = bitmap.Encode(SKEncodedImageFormat.Jpeg, option.Quality))
{
return p.ToArray();
}
@@ -366,9 +363,9 @@ namespace Lazy.Captcha.Core.Generator.Image
/// 文字的透明度
private float GenerateBlendPercentage(int frameIndex, int textIndex, int len)
{
- int num = frameIndex + textIndex;
- float r = (float)1 / (len - 1);
- float s = len * r;
+ var num = frameIndex + textIndex;
+ var r = (float)1 / (len - 1);
+ var s = len * r;
return num >= len ? (num * r - s) : num * r;
}
@@ -390,7 +387,7 @@ namespace Lazy.Captcha.Core.Generator.Image
:
new List();
- AnimatedGifEncoder gifEncoder = new AnimatedGifEncoder();
+ var gifEncoder = new AnimatedGifEncoder();
gifEncoder.Start();
gifEncoder.SetDelay(option.FrameDelay);
//-1:no repeat,0:always repeat
@@ -425,7 +422,7 @@ namespace Lazy.Captcha.Core.Generator.Image
// 绘制文字
DrawTexts(canvas, texGraphicDescriptions);
- using (SKData skData = bitmap.Encode(SKEncodedImageFormat.Jpeg, option.Quality))
+ using (var skData = bitmap.Encode(SKEncodedImageFormat.Jpeg, option.Quality))
{
using (var image = SKImage.FromEncodedData(skData))
{
diff --git a/Lazy.Captcha.Core/Lazy.Captcha.Core.csproj b/Lazy.Captcha.Core/Lazy.Captcha.Core.csproj
index 326120081a3a21b52fe5c6ceb3423d706b981632..cee3ef0222d5a115e60b196900ee47d101537f14 100644
--- a/Lazy.Captcha.Core/Lazy.Captcha.Core.csproj
+++ b/Lazy.Captcha.Core/Lazy.Captcha.Core.csproj
@@ -2,6 +2,7 @@
netstandard2.0
+ latest
仿EasyCaptcha和SimpleCaptcha,基于.Net Standard 2.0的图形验证码模块。版本2.0.0起绘图由ImageSharp调整为SkiaSharp。文档地址: https://gitee.com/pojianbing/lazy-captcha
https://gitee.com/pojianbing/lazy-captcha
https://gitee.com/pojianbing/lazy-captcha
diff --git a/Lazy.Captcha.xUnit/EvaluationEngineTest.cs b/Lazy.Captcha.xUnit/EvaluationEngineTest.cs
deleted file mode 100644
index 9454278169d6929a27923556ec93f6ee3744b990..0000000000000000000000000000000000000000
--- a/Lazy.Captcha.xUnit/EvaluationEngineTest.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-
-using Lazy.Captcha.Core;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Lazy.Captcha.xUnit
-{
- public class EvaluationEngineTest
- {
- private EvaluationEngine engine = new EvaluationEngine();
-
- [Fact]
- public void Test1()
- {
- var result = engine.Evaluate("1 + 1");
- Assert.Equal(2, result);
- }
-
- [Fact]
- public void Test2()
- {
- var result = engine.Evaluate("3 + 8");
- Assert.Equal(11, result);
- }
-
- [Fact]
- public void Test3()
- {
- var result = engine.Evaluate("11 + 5");
- Assert.Equal(16, result);
- }
-
- [Fact]
- public void Test4()
- {
- var result = engine.Evaluate("11 - 5");
- Assert.Equal(6, result);
- }
-
- [Fact]
- public void Test5()
- {
- var result = engine.Evaluate("7 - 9");
- Assert.Equal(-2, result);
- }
-
- [Fact]
- public void Test6()
- {
- var result = engine.Evaluate("0-0");
- Assert.Equal(0, result);
- }
-
- [Fact]
- public void Test7()
- {
- var result = engine.Evaluate("5*0");
- Assert.Equal(0, result);
- }
-
- [Fact]
- public void Test8()
- {
- var result = engine.Evaluate("5*3");
- Assert.Equal(15, result);
- }
-
- [Fact]
- public void Test9()
- {
- var result = engine.Evaluate("5*10");
- Assert.Equal(50, result);
- }
-
- [Fact]
- public void Test10()
- {
- var result = engine.Evaluate("8x2");
- Assert.Equal(16, result);
- }
- }
-}