6

memcacheベースのセッションのアイデアが好きではなく、ユーザーにGoogleアカウントを作成するように強制するというアイデアも好きではないため、GoogleAppEngineでCookieベースの認証/セッションを行う方法を探していました。 Webサイトを使用します。Tornadoフレームワークからの署名されたCookie関数について言及している誰かの投稿に出くわしましたが、それは私が必要としているもののようです。私が念頭に置いているのは、ユーザーのIDを改ざん防止Cookieに保存し、リクエストハンドラーにデコレーターを使用してユーザーの認証ステータスをテストすることです。副次的な利点として、ユーザーIDをリクエストハンドラーで使用できるようになります。データストアの仕事など。概念は、ASP.NETのフォーム認証に似ています。このコードは、Tornadoフレームワークのweb.pyモジュールから取得されます。

docstringsによると、「Cookieに署名してタイムスタンプを付け、偽造できないようにします」および「検証された場合は指定された署名済みCookieを返すか、Noneを返します」。

App Engineプロジェクトで使用しようとしましたが、リクエストハンドラのコンテキストでこれらのメソッドを機能させるための微妙な違いがわかりません。FriendFeed開発者が導入した機能を失うことなく、これを行う正しい方法を誰かに教えてもらえますか?set_secure_cookieとget_secure_cookieの部分が最も重要ですが、他のメソッドも使用できると便利です。

#!/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

パーツ: 1。uidは
キーの名前です2.1234は
明確な値です3.1234567890は
タイムスタンプ
です4.d32b9e9c67274fa062e2599fd659cc14は値とタイムスタンプから作成された署名です

4

5 に答える 5

12

TornadoApp Engineで動作することを意図したものではありませんでした(これは「独自のサーバー」です)。代わりに、「go」という言葉からApp Engine向けのフレームワークを選び、 tipfyなどの軽量でダンディなフレームワークを選んでみませんか。独自のユーザーシステム、またはApp Engine独自のOpenIn、OAuth、Facebookのいずれかを使用して認証を行います。安全なCookieまたはGAEデータストアを使用したセッション。さらに、WSGIとWerkzeugに基づく非常に軽量な「非フレームワーク」アプローチを採用しています。嫌いなものは何ですか?!users

于 2010-03-28T05:42:02.097 に答える
3

まだ探している人のために、ThriveSmartのAppEngineで使用できるTornadoCookieの実装のみを抽出しました。App Engineで正常に使用しており、今後も更新を続けていきます。

Cookieライブラリ自体は次の場所にあります:http: //github.com/thrivesmart/prayls/blob/master/prayls/lilcookies.py

含まれているサンプルアプリで実際の動作を確認できます。リポジトリの構造が変更された場合は、github.com / thrivesmart/prayls内でlilcookes.pyを探すことができます

それが誰かに役立つことを願っています!

于 2010-06-19T01:39:40.790 に答える
3

これは、誰かが興味を持っている場合に機能します。

from google.appengine.ext import webapp

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


class ExtendedRequestHandler(webapp.RequestHandler):
    """Extends the Google App Engine webapp.RequestHandler."""
    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 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):
        """Hashes a string based on a pass-phrase."""
        hash = hmac.new("MySecretPhrase",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.request.cookies:
            return self.request.cookies[name]
        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))
        new_cookie = Cookie.BaseCookie()
        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
        for morsel in new_cookie.values():
            self.response.headers.add_header('Set-Cookie',morsel.OutputString(None))

    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

次のように使用できます。

class MyHandler(ExtendedRequestHandler):
    def get(self):
        self.set_cookie(name="MyCookie",value="NewValue",expires_days=10)
        self.set_secure_cookie(name="MySecureCookie",value="SecureValue",expires_days=10)

        value1 = self.get_cookie('MyCookie')
        value2 = self.get_secure_cookie('MySecureCookie')
于 2010-07-25T01:50:25.823 に答える
0

ユーザーのユーザーIDのみをCookieに保存する場合(おそらくデータストアでユーザーのレコードを検索できるようにするため)、「安全な」Cookieや改ざん防止Cookieは必要ありません。必要なのは、十分な大きさの名前空間だけです。ユーザーIDの推測を非現実的にします(たとえば、GUIDやその他のランダムデータ)。

セッションストレージにデータストアを使用する、このための事前に作成されたオプションの1つは、Beakerです。または、ユーザーIDを保存するだけでよい場合は、set-cookie/cookieヘッダーを使用してこれを自分で処理することもできます。

于 2010-03-28T12:24:41.187 に答える
0

最近、誰かがTornadoから認証とセッションのコードを抽出し、GAE専用の新しいライブラリを作成しました。

おそらくこれはあなたが必要とする以上のものですが、彼らはGAEのために特別にそれを行ったので、あなたはそれを自分で適応させることを心配する必要はありません。

彼らのライブラリはgaemaと呼ばれています。2010年3月4日のGAEPythonグループでの発表は次のとおりです。http://groups.google.com/group/google-appengine-python/browse_thread/thread/d2d6c597d66ecad3/06c6dc49cb8eca0c?lnk = gst&q = tornado# 06c6dc49cb8eca0c

于 2010-03-29T03:48:11.430 に答える