我正在研究2.6中的python项目,该项目也有未来支持python 3的工作。具体来说,我正在研究digest-md5算法。
在没有运行此导入的python 2.6中:
from __future__ import unicode_literals
我能写一段代码,如:
a1 = hashlib.md5("%s:%s:%s" % (self.username, self.domain, self.password)).digest()
a1 = "%s:%s:%s" %(a1, challenge["nonce"], cnonce )
没有任何问题,我的身份验证工作正常。当我尝试导入unicode_literals的同一行代码时,我得到一个异常:
UnicodeDecodeError:'utf8'编解码器无法解码位置0的字节0xa8:意外的代码字节
现在我对python比较陌生,所以我有点陷入困境。如果我将格式化字符串中的%s替换为%r我能够连接字符串,但身份验证不起作用。我读过的digest-md5规范说明16字节二进制摘要必须附加到其他字符串上。
有什么想法吗?
答案 0 :(得分:6)
您观察到的行为的原因是from __future__ import unicode_literals
切换Python使用字符串的方式:
unicode_literals
将来,没有 u 前缀的字符串是在UCS-2或UCS-4中编码的unicode字符串(取决于编译Python时使用的编译器标志)。带有 b 前缀的字符串是数据类型bytes
的文字,与3.x之前的非unicode字符串非常相似。在任一版本的Python中,必须转换字节字符串和unicode字符串。默认情况下执行的转换取决于系统的默认字符集;在你的情况下,这是 UTF-8 。如果没有设置任何内容,它应该是 ascii ,它会拒绝\ x7f以上的所有字符。
hashlib.md5(...)返回的消息摘要.digest()是一个字节串,我想你也希望整个操作的结果也是一个字节串。如果需要,将nonce和cnonce-strings转换为byte-strings。:
a1 = hashlib.md5("%s:%s:%s" % (self.username, self.domain, self.password)).digest()
# note that UTF-8 may not be the encoding required by your counterpart, please check
a1 = b"%s:%s:%s" %(a1, challenge["nonce"].encode("UTF-8"), cnonce.encode("UTF-8") )
或者,您可以将来自调用的字节字符串转换为digest()
到unicode字符串(不推荐)。由于UCS-2的低8位相当于ISO-8859-1,这可能满足您的需求:
a1 = hashlib.md5("%s:%s:%s" % (self.username, self.domain, self.password)).digest()
a1 = "%s:%s:%s" %(a1.decode("ISO-8859-1"), challenge["nonce"], cnonce)
答案 1 :(得分:2)
问题是,导入unicode_literals后,“%s:%s:%s”变为unicode字符串。 散列的输出是“常规”字符串。 Python试图将常规字符串解码为unicode字符串并失败(正如预期的那样。散列输出应该看起来像噪声)。 将您的代码更改为:
a1 = a1 + str(':') + str(challenge["nonce"]) + str(':') + str(cnonce)
我假设cnonce
和challenge["nonce"]
是常规字符串。要更好地控制对字符串的转换(如果需要),请使用:
a1 += str(':') + challenge["nonce"].encode('UTF-8') + str(':') + cnonce.encode('UTF-8')