Random 多次 new 易因种子相同导致重复,应静态复用或用 Random.Shared;验证码禁用 Guid 和 DateTime,需限定无歧义字符集并用密码学安全随机源。
用 Random 生成随机字符串容易重复?别直接 new Random()
多次快速调用会拿到相同种子,导致连续生成的字符串一模一样。尤其在循环或高并发场景下特别明显。
- 每次 new
Random()都基于系统时钟,毫秒级精度不够,短时间新建多个实例几乎必然撞种子 - 正确做法是把
Random实例作为静态字段复用:private static readonly Random _random = new Random(); - 如果真需要线程安全(比如 ASP.NET Core 多请求并发),改用
Random.Shared(.NET 6+)或ThreadLocal<random></random>
生成验证码字符串时,为什么不能用 Guid.NewGuid().ToString()?
它虽然“随机”,但字符集太大(含 `-`、`0-9`、`a-f`),长度固定 32 位,且大小写混杂——对用户不友好,OCR 识别率低,还容易把 `0` 和 `O`、`l` 和 `1` 看混。
- 验证码应限制字符集:推荐只用
"23456789ABCDEFGHJKLMNPQRSTUVWXYZ"(去掉易混淆字符) - 长度建议 4–6 位,太短不安全,太长用户输入意愿下降
- 别用
Guid或DateTime.Now.Ticks拼接,它们不是密码学安全源,且可预测性高
需要真正不可预测的验证码?得换 RNGCryptoServiceProvider 或 RandomNumberGenerator
普通 Random 是伪随机,适合 UI 场景;但若验证码用于登录/支付等敏感环节,必须防暴力穷举或种子推测。
- .NET Core 3.0+ 推荐用
RandomNumberGenerator:RandomNumberGenerator.GetInt32(0, chars.Length) - 旧项目可用
RNGCryptoServiceProvider,但需手动 Dispose,已标记为过时 - 注意:性能比
Random低一个数量级,日常图形验证码没必要,但短信验证码建议上
中文环境下生成字符串,千万别用 Encoding.UTF8.GetBytes() 直接转
如果你试图靠“随机字节 → UTF8 字符串”来生成中文验证码,大概率得到乱码或异常——UTF-8 是变长编码,随机字节组合极大概率不构成合法字符。
- 中文验证码应预定义字符池:
private static readonly char[] _chineseChars = "一二三四五六七八九十".ToCharArray(); - 再用前面说的
RandomNumberGenerator或Random.Shared取索引,确保每个输出都是有效汉字 - 字体渲染支持很重要:确认前端用的字体包含这些字,否则显示成方框
Random 实例生命周期——这两点出错,验证码要么难认,要么形同虚设。


















