psk_validate.py
,提示用户输入潜在的密码,并检查其是否包含大小写字符,数字,特殊字符,并且长度至少为8个字符。据我所知,可以使用regex库来更有效地编写此代码,但是,我还没有学习到regex。
该程序似乎可以正常工作很好,对于这么小的程序,我认为不使用正则表达式也很好。
我想要有关此程序的所有反馈。特别是,我想知道编写而成的程序是否可以在实际应用中使用。我还想知道程序中是否存在任何逻辑错误和/或错误。
from sys import exit
def check_upper(input):
uppers = 0
upper_list = "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".split()
for char in input:
if char in upper_list:
uppers += 1
if uppers > 0:
return True
else:
return False
def check_lower(input):
lowers = 0
lower_list = "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".split()
for char in input:
if char in lower_list:
lowers += 1
if lowers > 0:
return True
else:
return False
def check_number(input):
numbers = 0
number_list = "1 2 3 4 5 6 7 8 9 0".split()
for char in input:
if char in number_list:
numbers += 1
if numbers > 0:
return True
else:
return False
def check_special(input):
specials = 0
special_list = "! @ $ % ^ & * ( ) _ - + = { } [ ] | \ , . > < / ? ~ ` \" ' : ;".split()
for char in input:
if char in special_list:
specials += 1
if specials > 0:
return True
else:
return False
def check_len(input):
if len(input) >= 8:
return True
else:
return False
def validate_password(input):
check_dict = {
'upper': check_upper(input),
'lower': check_lower(input),
'number': check_number(input),
'special': check_special(input),
'len' : check_len(input)
}
if check_upper(input) & check_lower(input) & check_number(input) & check_special(input) & check_len(input):
return True
else:
print "Invalid password! Review below and change your password accordingly!"
print
if check_dict['upper'] == False:
print "Password needs at least one upper-case character."
if check_dict['lower'] == False:
print "Password needs at least one lower-case character."
if check_dict['number'] == False:
print "Password needs at least one number."
if check_dict['special'] == False:
print "Password needs at least one special character."
if check_dict['len'] == False:
print "Password needs to be at least 8 characters in length."
print
while True:
password = raw_input("Enter desired password: ")
print
if validate_password(password):
print "Password meets all requirements and may be used."
print
print "Exiting program..."
print
exit(0)
#1 楼
概念强制性XKCD漫画,在我开始之前:
不再要求通过使用人类不友好的字符来增强密码强度。不过,我将按照编写代码的方式进行检查。
“显而易见”的简化方式
任何带有
if bool_expr: return True; else: return False
模式的代码都应简单地写为return bool_expr
。字符串可以直接迭代;无需先使用
.split()
将它们转换为列表。换句话说,如果您只写以下代码,则代码将起作用:upper_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
更好的是,您可以只使用
string.ascii_uppercase
。uppers += 1
计数循环可以使用sum()
内置函数可以更富表现力。实际上,在这种情况下,由于您只关心uppers > 0
,因此可以只使用any()
函数。经过这些更改,您的
check_upper()
函数变成了单行代码:def contains_upper(s):
return any(c in ascii_uppercase for c in s)
我已将
check_upper()
重命名为contains_upper()
,以明确表明该函数返回True
或False
。另外,请避免使用与内置函数名称一致的变量名,例如input
:如果您要使用input()
,可能会引起麻烦。代码复制
您的大多数
check_something()
功能是相同的。您应该概括而不是重复代码。from string import ascii_uppercase, ascii_lowercase, digits
def contains(required_chars, s):
return any(c in required_chars for c in s)
def contains_upper(s):
return contains(ascii_uppercase, s)
def contains_lower(s):
return contains(ascii_lowercase, s)
def contains_digit(s):
return contains(digits, s)
def contains_special(s):
return contains(r"""!@$%^&*()_-+={}[]|\,.></?~`"':;""", s)
def long_enough(s):
return len(s) >= 8
请注意,我使用了原始的长字符串来帮助处理标点符号字符串中的反斜杠。
validate_password()
check_dict
对您没有任何帮助。拥有五个布尔变量,您将再好不过了。您还将两次调用每个验证函数。&
(二进制按位与)运算符在这里不太合适。 and
(布尔AND)运算符会更合适。即使结果看起来相同,执行方式也不同:逻辑and
允许进行短路评估。 我个人就是这样写的,收集了所有失败消息的列表:
def validate_password(password):
VALIDATIONS = (
(contains_upper, 'Password needs at least one upper-case character.'),
(contains_lower, 'Password needs at least one lower-case character.'),
(contains_digit, 'Password needs at least one number.'),
(contains_special, 'Password needs at least one special character.'),
(long_enough, 'Password needs to be at least 8 characters in length.'),
)
failures = [
msg for validator, msg in VALIDATIONS if not validator(password)
]
if not failures:
return True
else:
print("Invalid password! Review below and change your password accordingly!\n")
for msg in failures:
print(msg)
print('')
return False
如果函数返回将
True
放在一个地方,那么为了保持一致性,最好在另一分支中返回False
而不是None
。自由浮动代码
通常将
if __name__ == '__main__':
围绕模块中不在函数内部的语句。这样,您可以通过执行import psk_validate
来将功能合并到另一个程序中,而无需实际运行该程序。如果正确地构造代码,很少或不需要调用
sys.exit(0)
。在这里,您只需要一个break
。if __name__ == '__main__':
while True:
password = raw_input("Enter desired password: ")
print()
if validate_password(password):
print("Password meets all requirements and may be used.\n")
print("Exiting program...\n")
break
评论
\ $ \ begingroup \ $
我认为也必须链接相关的Security.SE问题。 ;)+1
\ $ \ endgroup \ $
– jpmc26
17年6月8日,0:54
\ $ \ begingroup \ $
作为一名Python爱好者(不是专业人士),我很难阅读验证程序的味精,VALIDATIONS ...行中的味精。正如我头脑中解析为两个元素的列表一样,其中一个是用于验证程序的msg,另一个是在VALIDATIONS中的msg ...(这没有任何意义)。仅在验证器msg上加上括号,使(validator,msg)的行msg在VALIDATIONS中更容易阅读吗?
\ $ \ endgroup \ $
–奥利维尔·格雷戈尔(OlivierGrégoire)
17年6月8日在9:59
\ $ \ begingroup \ $
这个XKCD密码生成挑战也很重要:codegolf.stackexchange.com/questions/122756/…
\ $ \ endgroup \ $
–诺亚·克里斯蒂诺(Noah Cristino)
17年6月8日在11:46
\ $ \ begingroup \ $
@OlivierGrégoire我个人并没有在...中遇到(a,b),但是如果我确实遇到过,那么我会“很奇怪”,这会打破我的思路。
\ $ \ endgroup \ $
– Peilonrayz
17年6月8日在14:02
\ $ \ begingroup \ $
@OlivierGrégoire这是打开包装的一个例子。与x,y = [1,2]相同。循环版本在dict迭代中也很常见:对于d.items()中的k,v。该赋值通常不包含在括号中。您可能也会被列表理解这一事实所吸引。如果您在阅读它时遇到麻烦,我实际上建议在msg之后使用换行符,以将元素评估与循环迭代分开。现在,您已经知道了模式,您将开始更轻松地识别它。
\ $ \ endgroup \ $
– jpmc26
17年6月8日19:52
#2 楼
您可以使
check_upper
,check_lower
等全部使用一个函数,因此您想要创建诸如check_contains(input, letters)
之类的函数。可以通过以下方法进一步加以改进:
如果
return True
为true,请使用char in letters
早返回。您可以理解它。
使用(2)时,可以使用
any
达到与(1)相同的效果。 br /> 所以我要使用:
def check_contains(input, letters):
return any(char in letters for char in input)
我个人希望
validate_password(input)
只返回true或false,但是要与您的内容保持一致这样做,我会保留它以便打印。删除
sys.exit
,它不打算在程序中使用。而是使用break
来打破while循环。我将使用
getpass
而不是raw_input
来获取用户密码。这是因为它应该关闭echo,因此不会显示用户密码,因此其他人可以自己承担密码。您可以使用
strings
而不是手动写出字符串,,因此我将您的代码更改为:
from getpass import getpass
import string
def check_contains(input, letters):
return any(char in letters for char in input)
def validate_password(input):
valid = True
if not check_contains(input, string.ascii_uppercase):
valid = False
print "Password needs at least one upper-case character."
if not check_contains(input, string.ascii_lowercase):
valid = False
print "Password needs at least one lower-case character."
if not check_contains(input, string.digits):
valid = False
print "Password needs at least one number."
if not check_contains(input, string.punctuation + '#'):
valid = False
print "Password needs at least one special character."
if len(input) < 8:
valid = False
print "Password needs to be at least 8 characters in length."
return valid
while True:
password = getpass("Enter desired password: ")
if validate_password(password):
print "Valid password"
break
如果要使程序遵循SRP,则不应打印
validate_password
。因此,您可能需要使用以下内容。如果打印消息对您来说非常重要,那么那应该是验证密码是否正确的另一项功能。from getpass import getpass
import string
def check_contains(input, letters):
return any(char in letters for char in input)
def validate_password(input):
return all([
check_contains(input, string.ascii_uppercase),
check_contains(input, string.ascii_lowercase),
check_contains(input, string.digits),
check_contains(input, string.punctuation + '#'),
len(input) >= 8
])
while True:
password = getpass("Enter desired password: ")
if validate_password(password):
print "Valid password"
break
else:
print "invalid password"
评论
\ $ \ begingroup \ $
如文档中所述,您可以使用string.ascii_lowercase,string.ascii_uppercase替换硬编码字母,并使用string.digits替换数字。
\ $ \ endgroup \ $
– grundic
17年7月7日在18:49
\ $ \ begingroup \ $
@grundic我进行了编辑以添加该内容,我不想记住它是“小写”而不是“小写”,因此我通常跳过使用它; P
\ $ \ endgroup \ $
– Peilonrayz
17年7月7日在18:53
\ $ \ begingroup \ $
\ $ \ endgroup \ $
– grundig
17年6月7日在19:02
\ $ \ begingroup \ $
@grundic谢谢,我合理地忘记添加它们,);
\ $ \ endgroup \ $
– Peilonrayz
17年7月7日在19:04
\ $ \ begingroup \ $
如果需要验证许多密码,则可能要为字符集定义全局常量集。
\ $ \ endgroup \ $
–地狱
17年6月8日在6:20
评论
相关:blogs.dropbox.com/tech/2012/04/…