2010-02-09 39 views
16

我正在寫用戶註冊我自己的驗證碼系統。所以我需要創建一個合適的URL來接收生成的驗證碼圖片。代看起來像這樣:Django的urlsafe base64解密與解密

_cipher = cipher.new(settings.CAPTCHA_SECRET_KEY, cipher.MODE_ECB) 
_encrypt_block = lambda block: _cipher.encrypt(block + ' ' * (_cipher.block_size - len(block) % _cipher.block_size)) 
#... 
a = (self.rightnum, self.animal_type[1]) 
serialized = pickle.dumps(a) 
encrypted = _encrypt_block(serialized) 
safe_url = urlsafe_b64encode(encrypted) 

但後來我想接收通過視圖功能GET請求該鍵,它失敗在urlsafe_b64decode()與「字符映射必須返回整數,無或Unicode」的錯誤:

def captcha(request): 
    try: 
    key = request.REQUEST['key'] 
    decoded = urlsafe_b64decode(key) 
    decrypted = _decrypt_block(decoded) 
    deserialized = pickle.loads(decrypted) 
    return HttpResponse(deserialized) 
    except KeyError: 
    return HttpResponseBadRequest() 

我發現,urlsafe_b64encode的輸出有一個海峽,但GET請求返回一個Unicode對象(不過這是一個正確的字符串)。 Str()沒有幫助(它在django內部深處返回解碼錯誤),並且如果我使用密鑰。 repr它的工作原理,但解密器不會出現錯誤「輸入字符串的長度必須是16的倍數」。 在一個測試文件裏面,所有這些構造完美地工作,我不明白,有什麼不對?

+0

是否需要加密?無法生成驗證碼在會話數據服務器端存儲正確答案?或者你想避免這種情況? – MattH

+0

只是沒有考慮到它。但是這個問題很有趣,我想了解它爲什麼會發生。 – Enchantner

+0

'key = str(request.REQUEST ['key'])'不起作用? – MattH

回答

32

問題是,顯式的b64decode只能取字節(一個字符串),而不是unicode。

>>> import base64 
>>> test = "Hi, I'm a string" 
>>> enc = base64.urlsafe_b64encode(test) 
>>> enc 
'SGksIEknbSBhIHN0cmluZw==' 
>>> uenc = unicode(enc) 
>>> base64.urlsafe_b64decode(enc) 
"Hi, I'm a string" 
>>> base64.urlsafe_b64decode(uenc) 
Traceback (most recent call last): 
... 
TypeError: character mapping must return integer, None or unicode 

既然你知道你的數據只包含ASCII數據(這就是base64encode將返回),它應該是安全的編碼您的Unicode代碼點作爲ASCII或UTF-8字節,這些字節將等同於您期望的ASCII碼。

>>> base64.urlsafe_b64decode(uenc.encode("ascii")) 
"Hi, I'm a string" 
+2

這有助於我在python webserver中遇到的相關問題。 – hughdbrown

+1

「你知道你的數據只包含ASCII數據」 - 這就是爲什麼我會寫'.encode(「ascii」)'。 –

+0

是的,「ascii」會更好。更新。 –

3

我解決了這個問題!

deserialized = pickle.loads(captcha_decrypt(urlsafe_b64decode(key.encode('ascii')))) 
return HttpResponse(str(deserialized)) 

但是我還是不明白,爲什麼它不是第一次工作。