python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python密码生成器

基于Python编写一个强密码随机生成器

作者:溪语流沙

这篇文章主要为大家详细介绍了如何基于Python编写一个强密码随机生成器,支持长度可选、字符集配置、一键复制,有需要的小伙伴可以参考一下

“随机”和“安全”是一回事吗?——不是。

这个区别,恰好是本篇最大的知识点,也是很多写了三年Python的人没想过的问题。

本篇成品:一个80行上下的密码生成器。长度可选、字符集可配、一键进剪贴板,用“密码学级”的随机数保证每个密码都不可预测。

太长不看版(给想快速上手的你)

项目信息一句话说明
项目名称强密码生成器(Password Generator)
代码行数~80行(含注释)
依赖第一个第三方包pyperclip(剪贴板功能)
核心功能长度可选、字符集可配、一键复制、密码学安全
跑起来的命令python main.py(默认16位)
核心知识点secrets(真随机)≠ random(伪随机)
做完你能得到一个可以天天用的密码工具

一、先做一个实验:random的“伪”字怎么理解

打开终端,亲手跑这两行:

import random
random.seed(42)
print(random.random())

输出:0.6394267984578837

关掉重跑一遍——还是这个数。 只要种子相同,random的“随机”序列就一模一样,因为它用的是数学公式(梅森旋转算法)模拟出来的随机,原理上只要观察到足够多的输出就能被推断。

这就是“伪随机”。对游戏骰子、抽样模拟毫无问题;但用来生成密码就是灾难——攻击者推断出你的随机规律,等于你的密码有了出厂说明书。

所以Python专门提供了secrets模块

对比维度random(伪随机)secrets(真随机)
随机来源数学公式(种子决定)操作系统真实熵(鼠标移动、磁盘抖动等物理噪声)
能否复现✅ 种子相同即可复现❌ 无法复现
适用场景游戏、模拟、抽样密码、令牌、安全相关一切
Python官方建议“不应用于安全目的”“用于生成密码、令牌等”

一句话记住:决定“游戏里怪走哪条路”用random,决定“你的密码是什么”用secrets

二、成品预览

$ python main.py
生成的密码:k7#mQx2$Lp9@vR4w
$ python main.py -l 24 --no-symbols
生成的密码:hT3nBq8Xw2mZ5rK9pL4vE7cA
$ python main.py -l 12 --no-symbols --no-upper
生成的密码:f4k2m9x1q7wz
$ python main.py -c
生成的密码:k7#mQx2$Lp9@vR4w
已复制到剪贴板 ✅

四个需求全满足

需求参数说明
长度可选-l 24默认16位
字符集可配--no-symbols / --no-upper默认全启用
一键进剪贴板-c复制后直接Ctrl+V粘贴
密码学安全secrets真随机,不可预测

三、前置知识:两个标准库模块

string模块 —— 现成的字符集常量表

不用手打a到z,直接用:

常量内容
string.ascii_lowercaseabcdefghijklmnopqrstuvwxyz
string.ascii_uppercaseABCDEFGHIJKLMNOPQRSTUVWXYZ
string.digits0123456789
string.punctuation全部英文符号(本篇自选了一个更温和的子集)

secrets三件套

本篇用到两个,第三个预告一下:

函数作用
secrets.choice(序列)从序列里安全随机挑一个
secrets.randbelow(n)返回0到n-1的安全随机整数
secrets.token_hex()生成十六进制令牌(第23篇FastAPI会用到)

四、第0步:setup——第一个第三方依赖登场

1. 新建文件夹passwd_gen,VSCode打开

2. python -m venv venv && 激活venv

3. git init + .gitignore

pip install pyperclip
pip freeze > requirements.txt     # 立刻存档,从第一个依赖就养成习惯

看到requirements.txt里多出pyperclip==x.x.x这行,你就明白第3篇的“存档/读档”不是纸上谈兵。

五、第1步:字符池——按开关拼装弹药库

"""passwd —— 强密码生成器"""
import argparse
import secrets
import string

CHARSETS = {
    "lower":   string.ascii_lowercase,   # a-z
    "upper":   string.ascii_uppercase,   # A-Z
    "digits":  string.digits,            # 0-9
    "symbols": "!@#$%^&*-_=+?",          # 自选的安全符号子集
}


def build_pool(use):
    """按开关拼出字符池,并抽出"每类保底"的必备字符"""
    pool = ""
    required = []
    for name, chars in CHARSETS.items():
        if use[name]:
            pool += chars
            required.append(secrets.choice(chars))
    return pool, required

required的设计意图:如果用户要“含数字”的密码,就不能靠运气——每启用的类别保底放一个,剩下的再从全池随机补。这是密码生成器的行业惯例。

六、第2步:安全洗牌——random.shuffle也不行(重点!)

有个隐蔽的问题:保底字符永远排在密码开头(第一位小写、第二位大写……),规律等于白保底。所以要洗牌。

但注意——random.shuffle()也不行,它内部还是伪随机。三行自己写一个secrets版:

def secure_shuffle(items):
    """secrets版洗牌(Fisher-Yates):random.shuffle的随机性不够"硬" """
    for i in range(len(items) - 1, 0, -1):
        j = secrets.randbelow(i + 1)
        items[i], items[j] = items[j], items[i]
    return items

这是经典的Fisher-Yates洗牌:从尾往前,每个位置和一个随机更早的位置交换。算法课上的知识,在这里真的用上了。

七、第3步:generate——保底、补齐、洗牌

def generate(length, use):
    pool, required = build_pool(use)
    if not pool:
        raise ValueError("至少要启用一类字符")
    if length < len(required):
        raise ValueError(f"长度至少要 {len(required)},才能保证每类字符都出现")

    chars = required[:]                                          # 每类保底一个
    chars += [secrets.choice(pool) for _ in range(length - len(required))]
    return "".join(secure_shuffle(chars))                        # 洗牌后拼接

两个raise ValueError的意义

校验场景作用
if not pool四个--no-xxx全开字符池为空,没法生成
if length < len(required)-l 2还要四类字符保底数学上不可能

报错信息写清楚原因——这是把用户当人看。不是程序崩溃,是帮用户发现不合理的需求。

八、第4步:argparse + 剪贴板

def main():
    parser = argparse.ArgumentParser(description="强密码生成器")
    parser.add_argument("-l", "--length", type=int, default=16,
                        help="密码长度,默认16")
    parser.add_argument("--no-lower", action="store_true", help="不用小写字母")
    parser.add_argument("--no-upper", action="store_true", help="不用大写字母")
    parser.add_argument("--no-digits", action="store_true", help="不用数字")
    parser.add_argument("--no-symbols", action="store_true", help="不用符号")
    parser.add_argument("-c", "--copy", action="store_true",
                        help="生成后自动复制到剪贴板")
    args = parser.parse_args()

    use = {"lower": not args.no_lower, "upper": not args.no_upper,
           "digits": not args.no_digits, "symbols": not args.no_symbols}

    try:
        password = generate(args.length, use)
    except ValueError as e:
        print(f"参数有问题:{e}")
        return
    print(f"生成的密码:{password}")

    if args.copy:
        try:
            import pyperclip
            pyperclip.copy(password)
            print("已复制到剪贴板 ✅")
        except ImportError:
            print("未安装pyperclip:pip install pyperclip后可自动复制")


if __name__ == "__main__":
    main()

两个设计细节

设计写法为什么
开关反向--no-xxx默认全启用,常用路径最短
剪贴板容错try/except ImportError没装pyperclip照样出密码——可选功能不许拖死核心功能

九、第5步:验收清单

python main.py                      # 默认16位,四类字符都有
python main.py -l 24 --no-symbols   # 24位无符号,适合死板的网站
python main.py -l 12 --no-symbols --no-upper
python main.py -c                   # 去Ctrl+V粘贴试试
python main.py -l 2                 # 应该看到友好的报错,而不是崩溃

提交Git

git add .
git commit -m "完成密码生成器v1:secrets随机 + 剪贴板"

十、常见报错:这6个,安全类工具的特色坑(重点!)

ModuleNotFoundError: No module named 'pyperclip'

原因:装了还是报错?八成装进了别的环境

解法:第3篇的排查三连回归——

  1. 看提示符有没有(venv)
  2. python -m pip list确认pyperclip在列表里;
  3. 不行就python -m pip install pyperclip重装。

参数有问题:至少要启用一类字符

原因:你把四个--no-xxx全开了,字符池为空。校验在拦你。

解法:至少留一类字符。这不是bug,是第7步那句raise ValueError在站岗。

参数有问题:长度至少要 4

原因-l 2还想四类字符保底?数学上不可能。

解法:要么加长度,要么减类别。

生成的密码开头永远是小写、大写、数字、符号的顺序

原因:你忘了洗牌,或者用了random.shuffle

解法:确认调用的是secure_shuffle而不是random.shuffle。用断点在return前看一眼chars,洗牌前后一对比就明白。

Linux下报PyperclipException

原因:pyperclip在Linux需要xclip或xsel支持。

解法

sudo apt install xclip    # Ubuntu/Debian
# 或
sudo apt install xsel

Windows/macOS开箱即用。

某些网站死活不接受生成的密码

原因:不少老系统只允许有限的符号(比如只认!@#,不认$%^)。

解法

这就是CHARSETS里“自选符号子集”存在的原因——生成器迁就规则,而不是反过来。数据驱动的好处又见面了。

十一、课后练习

#练习难度提示
1强度可视化:打印“熵约Nbit”⭐⭐length * math.log2(len(pool));经验值80bit以上算强
2记忆型密码:支持“四单词模式”,如tiger-apple-moon-river⭐⭐单词表 + '-'.join(secrets.choice(words) for _ in range(4))
3批量模式:加--count 5一次生成5个⭐⭐for循环调generate
4(选做)排除易混淆字符:0/O、1/l长得太像⭐⭐⭐从字符池里.replace()掉即可

一个安全彩蛋

顺手把本篇的知识串起来:生成密码只是第一步,密码本身的存放同样是安全问题。

所以:

这个后续会正式展开:用户系统里的密码为什么必须“哈希加盐”存储。

你现在只需要记住军规——明文密码不进仓库.gitignore的最后三行就是为它准备的)。

以上就是基于Python编写一个强密码随机生成器的详细内容,更多关于Python密码生成器的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文