Tôi đã tìm kiếm một cách để thực hiện xác thực/phiên dựa trên cookie trong Google App Engine vì tôi không thích ý tưởng về các phiên dựa trên memcache và tôi cũng không thích ý tưởng buộc người dùng tạo tài khoản google chỉ để sử dụng trang web. Tôi tình cờ gặp một người nào đó của posting đã đề cập đến một số chức năng cookie đã ký từ khung Tornado và nó trông giống như những gì tôi cần. Điều tôi lưu ý là lưu trữ id của người dùng trong cookie bằng chứng giả mạo và có thể sử dụng trình trang trí cho trình xử lý yêu cầu để kiểm tra trạng thái xác thực của người dùng và như một lợi ích phụ mà id người dùng sẽ có sẵn cho trình xử lý yêu cầu công việc kho dữ liệu và như vậy. Khái niệm này sẽ tương tự như các hình thức xác thực trong ASP.NET. Mã này đến từ mô đun web.py của khung Tornado.Google App Engine - Cookie an toàn
Theo các tài liệu, nó "Ký tên và đặt thời gian cho một cookie để nó không thể được giả mạo" và "Trả về cookie đã ký nếu nó xác nhận hoặc Không."
Tôi đã cố gắng sử dụng nó trong một dự án App Engine, nhưng tôi không hiểu các sắc thái của việc cố gắng làm cho các phương thức này hoạt động trong ngữ cảnh của trình xử lý yêu cầu. Ai đó có thể cho tôi thấy đúng cách để làm điều này mà không làm mất chức năng mà các nhà phát triển FriendFeed đưa vào nó? Các phần set_secure_cookie và get_secure_cookie là quan trọng nhất, nhưng nó sẽ rất tốt để có thể sử dụng các phương thức khác.
#!/usr/bin/env python
import Cookie
import base64
import time
import hashlib
import hmac
import datetime
import re
import calendar
import email.utils
import logging
def _utf8(s):
if isinstance(s, unicode):
return s.encode("utf-8")
assert isinstance(s, str)
return s
def _unicode(s):
if isinstance(s, str):
try:
return s.decode("utf-8")
except UnicodeDecodeError:
raise HTTPError(400, "Non-utf8 argument")
assert isinstance(s, unicode)
return s
def _time_independent_equals(a, b):
if len(a) != len(b):
return False
result = 0
for x, y in zip(a, b):
result |= ord(x)^ord(y)
return result == 0
def cookies(self):
"""A dictionary of Cookie.Morsel objects."""
if not hasattr(self,"_cookies"):
self._cookies = Cookie.BaseCookie()
if "Cookie" in self.request.headers:
try:
self._cookies.load(self.request.headers["Cookie"])
except:
self.clear_all_cookies()
return self._cookies
def _cookie_signature(self,*parts):
self.require_setting("cookie_secret","secure cookies")
hash = hmac.new(self.application.settings["cookie_secret"],
digestmod=hashlib.sha1)
for part in parts:hash.update(part)
return hash.hexdigest()
def get_cookie(self,name,default=None):
"""Gets the value of the cookie with the given name,else default."""
if name in self.cookies:
return self.cookies[name].value
return default
def set_cookie(self,name,value,domain=None,expires=None,path="/",
expires_days=None):
"""Sets the given cookie name/value with the given options."""
name = _utf8(name)
value = _utf8(value)
if re.search(r"[\x00-\x20]",name + value):
# Don't let us accidentally inject bad stuff
raise ValueError("Invalid cookie %r:%r" % (name,value))
if not hasattr(self,"_new_cookies"):
self._new_cookies = []
new_cookie = Cookie.BaseCookie()
self._new_cookies.append(new_cookie)
new_cookie[name] = value
if domain:
new_cookie[name]["domain"] = domain
if expires_days is not None and not expires:
expires = datetime.datetime.utcnow() + datetime.timedelta(
days=expires_days)
if expires:
timestamp = calendar.timegm(expires.utctimetuple())
new_cookie[name]["expires"] = email.utils.formatdate(
timestamp,localtime=False,usegmt=True)
if path:
new_cookie[name]["path"] = path
def clear_cookie(self,name,path="/",domain=None):
"""Deletes the cookie with the given name."""
expires = datetime.datetime.utcnow() - datetime.timedelta(days=365)
self.set_cookie(name,value="",path=path,expires=expires,
domain=domain)
def clear_all_cookies(self):
"""Deletes all the cookies the user sent with this request."""
for name in self.cookies.iterkeys():
self.clear_cookie(name)
def set_secure_cookie(self,name,value,expires_days=30,**kwargs):
"""Signs and timestamps a cookie so it cannot be forged"""
timestamp = str(int(time.time()))
value = base64.b64encode(value)
signature = self._cookie_signature(name,value,timestamp)
value = "|".join([value,timestamp,signature])
self.set_cookie(name,value,expires_days=expires_days,**kwargs)
def get_secure_cookie(self,name,include_name=True,value=None):
"""Returns the given signed cookie if it validates,or None"""
if value is None:value = self.get_cookie(name)
if not value:return None
parts = value.split("|")
if len(parts) != 3:return None
if include_name:
signature = self._cookie_signature(name,parts[0],parts[1])
else:
signature = self._cookie_signature(parts[0],parts[1])
if not _time_independent_equals(parts[2],signature):
logging.warning("Invalid cookie signature %r",value)
return None
timestamp = int(parts[1])
if timestamp < time.time() - 31 * 86400:
logging.warning("Expired cookie %r",value)
return None
try:
return base64.b64decode(parts[0])
except:
return None
uid = 1234 | 1234567890 | d32b9e9c67274fa062e2599fd659cc14
Parts:
1. uid là tên của khóa
2. 1234 là giá trị của mình rõ ràng
3. 1234567890 là timestamp
4. d32b9e9c67274fa062e2599fd659cc14 là chữ ký được tạo từ giá trị và dấu thời gian
tôi đã không có ý định sử dụng Tornado với App Engine, tôi chỉ muốn thiết lập và lấy đã ký cookie theo cách họ đã làm. Tôi đã xem xét mã cookie bảo mật của mẹo/werkzeug và tôi nghĩ rằng những gì họ đang làm ở Tornado thanh lịch hơn. – tponthieux