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); - } - } -}