我的登录/登出与Facebook的错误

时间:2011-11-22 09:40:34

标签: python debugging google-app-engine facebook-graph-api facebook

我的facebook for websites中有一个有趣而奇怪的错误。当我使用facebook登录用户时,作为用户,我必须按重新加载以从cookie获取用户数据。否则cookie无法找到用户。如果我在登录后按一次重新加载并在注销后重新加载一次我可以登录并注销,但这表明我做错了。你能帮我找到这个bug吗?

我使用了https://gist.github.com/1190267中的代码并尝试记录Cookie查找,但是第一次找不到用户:

def get_user_from_cookie(cookies, app_id, app_secret):
    """Parses the cookie set by the official Facebook JavaScript SDK.

    cookies should be a dictionary-like object mapping cookie names to
    cookie values.

    If the user is logged in via Facebook, we return a dictionary with the
    keys "uid" and "access_token". The former is the user's Facebook ID,
    and the latter can be used to make authenticated requests to the Graph API.
    If the user is not logged in, we return None.

    Download the official Facebook JavaScript SDK at
    http://github.com/facebook/connect-js/. Read more about Facebook
    authentication at http://developers.facebook.com/docs/authentication/.
    """
    logging.debug('getting user from cookie')
    cookie = cookies.get("fbsr_" + app_id, "")
    if not cookie:
        logging.debug('no cookie found')
        return None

我使用的登录网址是 https://www.facebook.com/dialog/oauth?client_id=164355773607006&redirect_uri=http://www.koolbusiness.com

并且记录登录方案在重新加载之前不会获取cookie:

"GET /?code=AQB9sh9RWdZsUC_TBWFHLOnOKehjk2ls6kN1ZzCBQRFa6s2ra58e5slaBSI8lYwC5q9Q_f524nsrF-Ts-mgxAHc9xIvt3U7rufKlfJuNfkRbGwgPWZZLCYCwnWHPdb00ANd8QOHB_bYMaI-R_mdI3nQW6bRvpD0DR-SOW-jSvhS8bel4_KlzaBFY3DnYNvxhJgY HTTP/1.1" 200 6248 - "Mozilla/5.0 (X11; Linux x86_64; rv:2.0) Gecko/20100101 Firefox/4.0" "www.koolbusiness.com" ms=80 cpu_ms=0 api_cpu_ms=0 cpm_usd=0.000777 instance=00c61b117c460a7d3f730b42451a4153b74e

D 2011-11-22 07:36:28.182

getting user from cookie

D 2011-11-22 07:36:28.183

no cookie found

为什么呢?同样,当我尝试注销时,我必须做两次,我无法看到错误的位置。我一直在努力使用尽可能多的服务器,我怀疑我的问题是处理cookie。你能告诉我该怎么办?我设置cookie的功能是:

def set_cookie(self, name, value, expires=None):

    if value is None:
        value = 'deleted'
        expires = datetime.timedelta(minutes=-50000)
    jar = Cookie.SimpleCookie()
    jar[name] = value
    jar[name]['path'] = '/'
    if expires:
        if isinstance(expires, datetime.timedelta):
            expires = datetime.datetime.now() + expires
        if isinstance(expires, datetime.datetime):
            expires = expires.strftime('%a, %d %b %Y %H:%M:%S')
        jar[name]['expires'] = expires
    self.response.headers.add_header(*jar.output().split(': ', 1))

以下是2个应该为我做的课程。正如我所说,如果我重新加载,一切都有效,这是非常奇怪的,因为在登录后没有设置cookie,并且只是通过在FB登录后重新加载我的索引pge来设置cookie。

谢谢

class BaseHandler(webapp2.RequestHandler):
    facebook = None
    user = None
    csrf_protect = True

    @property
    def current_user(self):
        if not hasattr(self, "_current_user"):
            self._current_user = None
            cookie = facebook.get_user_from_cookie(
                self.request.cookies, facebookconf.FACEBOOK_APP_ID, facebookconf.FACEBOOK_APP_SECRET)
        logging.debug("logging cookie"+str(cookie))
            if cookie:
                # Store a local instance of the user data so we don't need
                # a round-trip to Facebook on every request
                user = FBUser.get_by_key_name(cookie["uid"])
                logging.debug("user "+str(user))

                if not user:
                    graph = facebook.GraphAPI(cookie["access_token"])
                    profile = graph.get_object("me")
                    user = FBUser(key_name=str(profile["id"]),
                                id=str(profile["id"]),
                                name=profile["name"],
                                profile_url=profile["link"],
                                access_token=cookie["access_token"])
                    user.put()
                elif user.access_token != cookie["access_token"]:
                    user.access_token = cookie["access_token"]
                    user.put()
                self._current_user = user
        return self._current_user

    @property
    def current_sender(self):
        if not hasattr(self, "_current_sender"):
            self._current_sender = None
        host=os.environ.get('HTTP_HOST', os.environ['SERVER_NAME'])
            if host.find('.br') > 0:
            sender = 'info@montao.com.br'
        else:
            sender = 'admin@koolbusiness.com'

        self._current_sender = sender
        return self._current_sender

    @property
    def current_logo(self):
        if not hasattr(self, "_current_logo"):
            self._current_logo = None

        self._current_logo = os.environ.get('HTTP_HOST', os.environ['SERVER_NAME'])
        return self._current_logo

    def initialize(self, request, response):
        """General initialization for every request"""
        super(BaseHandler, self).initialize(request, response)

        try:
            self.init_facebook()
            self.init_csrf()
            self.response.headers['P3P'] = 'CP=HONK' # iframe cookies in IE
        except Exception, ex:
            self.log_exception(ex)
            raise

    def handle_exception(self, ex, debug_mode):
        """Invoked for unhandled exceptions by webapp"""
        self.log_exception(ex)
        self.render('error',
            trace=traceback.format_exc(), debug_mode=debug_mode)

    def log_exception(self, ex):
        """Internal logging handler to reduce some App Engine noise in errors"""
        msg = ((str(ex) or ex.__class__.__name__) +
                ': \n' + traceback.format_exc())
        if isinstance(ex, urlfetch.DownloadError) or \
           isinstance(ex, DeadlineExceededError) or \
           isinstance(ex, CsrfException) or \
           isinstance(ex, taskqueue.TransientError):
            logging.warn(msg)
        else:
            logging.error(msg)

    def set_cookie(self, name, value, expires=None):

        if value is None:
            value = 'deleted'
            expires = datetime.timedelta(minutes=-50000)
        jar = Cookie.SimpleCookie()
        jar[name] = value
        jar[name]['path'] = '/'
        if expires:
            if isinstance(expires, datetime.timedelta):
                expires = datetime.datetime.now() + expires
            if isinstance(expires, datetime.datetime):
                expires = expires.strftime('%a, %d %b %Y %H:%M:%S')
            jar[name]['expires'] = expires
        self.response.headers.add_header(*jar.output().split(': ', 1))

    def render_jinja(self, name, **data):

        logo = 'Koolbusiness.com'
        logo_url = '/_/img/kool_business.png'
        analytics = 'UA-3492973-18'
        domain = 'koolbusiness'

        if get_host().find('.br') > 0:
            cookie_django_language = 'pt-br'
        logo = 'Montao.com.br'
            logo_url = '/_/img/montao_small.gif'
            analytics = 'UA-637933-12'
        domain = None
        elif get_host().find('allt') > 0 and not self.request.get('hl'):
            logo = ''
            cookie_django_language = 'sv'
        elif get_host().find('gralumo') > 0 \
            and not self.request.get('hl'):
            cookie_django_language = 'es_AR'
        else:
            cookie_django_language = self.request.get('hl', '')
        if cookie_django_language:
            if cookie_django_language == 'unset':
                del self.request.COOKIES['django_language']
            else:
        self.set_cookie('django_language', cookie_django_language)
            translation.activate(cookie_django_language)

        """Render a Jinja2 template"""
        if not data:
            data = {}
        data['js_conf'] = json.dumps({
            'appId': facebookconf.FACEBOOK_APP_ID,
            'canvasName': facebookconf.FACEBOOK_CANVAS_NAME,
            'userIdOnServer': self.user.id if self.user else None,
        })
        data['logged_in_user'] = self.user
        data['message'] = self.get_message()
        data['csrf_token'] = self.csrf_token
        data['canvas_name'] = facebookconf.FACEBOOK_CANVAS_NAME
        data['current_user']=self.current_user 
    gkeys = ''
        if os.environ.get('HTTP_HOST'):
            url = os.environ['HTTP_HOST']
        else:
            url = os.environ['SERVER_NAME']

        data['user']=users.get_current_user()
        data['facebook_app_id']=facebookconf.FACEBOOK_APP_ID
    user = users.get_current_user()
        data['logout_url']=users.create_logout_url(self.request.uri) if users.get_current_user() else 'https://www.facebook.com/dialog/oauth?client_id='+facebookconf.FACEBOOK_APP_ID+'&redirect_uri='+self.request.uri
        host=os.environ.get('HTTP_HOST', os.environ['SERVER_NAME'])
        data['host']=host
        if host.find('.br') > 0:
        logo = 'Montao.com.br'
            logo_url = '/_/img/montao_small.gif'
            analytics = 'UA-637933-12'
        domain = None
    else:
        logo = 'Koolbusiness.com'
            logo_url = '/_/img/kool_business.png'
            analytics = 'UA-3492973-18'
        domain = 'koolbusiness'

        data['domain']=domain
        data['analytics']=analytics
        data['logo']=logo
        data['logo_url']=logo_url
        data['admin']=users.is_current_user_admin()
        if user:
            data['greeting'] = ("Welcome, %s! (<a href=\"%s\">sign out</a>)" %
                        (user.nickname(), users.create_logout_url("/")))

    template = jinja_environment.get_template('templates/'+name+'.html')
        self.response.out.write(template.render(data))

    """
        self.response.out.write(template.render(
            os.path.join(
                os.path.dirname(__file__), 'templates', name + '.html'),
            data))
        """

    def render(self, name, **data):

        logo = 'Koolbusiness.com'
        logo_url = '/_/img/kool_business.png'
        analytics = 'UA-3492973-18'
        domain = 'koolbusiness'

        if get_host().find('.br') > 0:
            cookie_django_language = 'pt-br'
        logo = 'Montao.com.br'
            logo_url = '/_/img/montao_small.gif'
            analytics = 'UA-637933-12'
        domain = None
        elif get_host().find('allt') > 0 and not self.request.get('hl'):
            logo = ''
            cookie_django_language = 'sv'
        elif get_host().find('gralumo') > 0 \
            and not self.request.get('hl'):
            cookie_django_language = 'es_AR'
        else:
            cookie_django_language = self.request.get('hl', '')
        if cookie_django_language:
            if cookie_django_language == 'unset':
                del self.request.COOKIES['django_language']
            else:
        self.set_cookie('django_language', cookie_django_language)
            translation.activate(cookie_django_language)

        """Render a template"""
        if not data:
            data = {}
        data['js_conf'] = json.dumps({
            'appId': facebookconf.FACEBOOK_APP_ID,
            'canvasName': facebookconf.FACEBOOK_CANVAS_NAME,
            'userIdOnServer': self.user.id if self.user else None,
        })
        data['logged_in_user'] = self.user
        data['message'] = self.get_message()
        data['csrf_token'] = self.csrf_token
        data['canvas_name'] = facebookconf.FACEBOOK_CANVAS_NAME
        data['current_user']=self.current_user 
        data['user']=users.get_current_user()
        data['facebook_app_id']=facebookconf.FACEBOOK_APP_ID
    user = users.get_current_user()
        data['logout_url']=users.create_logout_url(self.request.uri) if users.get_current_user() else 'https://www.facebook.com/dialog/oauth?client_id='+facebookconf.FACEBOOK_APP_ID+'&redirect_uri='+self.request.uri
        host=os.environ.get('HTTP_HOST', os.environ['SERVER_NAME'])
        data['host']=host
        if not host.find('.br') > 0:
        logo = 'Koolbusiness.com'
            logo_url = '/_/img/kool_business.png'
            analytics = 'UA-3492973-18'
        domain = 'koolbusiness'

        data['domain']=domain
        data['analytics']=analytics
        data['logo']=logo
        data['logo_url']=logo_url
        data['admin']=users.is_current_user_admin()
        if user:
            data['greeting'] = ("Welcome, %s! (<a href=\"%s\">sign out</a>)" %
                        (user.nickname(), users.create_logout_url("/")))

    gkeys = ''
        if os.environ.get('HTTP_HOST'):
            url = os.environ['HTTP_HOST']
        else:
            url = os.environ['SERVER_NAME']

        self.response.out.write(template.render(
            os.path.join(
                os.path.dirname(__file__), 'templates', name + '.html'),
            data))

    def init_facebook(self):

        facebook = Facebook()
        user = None

        # initial facebook request comes in as a POST with a signed_request
        if 'signed_request' in self.request.POST:
            facebook.load_signed_request(self.request.get('signed_request'))
            # we reset the method to GET because a request from facebook with a
            # signed_request uses POST for security reasons, despite it
            # actually being a GET. in webapp causes loss of request.POST data.
            self.request.method = 'GET'
            #self.set_cookie(
                #'', facebook.user_cookie, datetime.timedelta(minutes=1440))
        elif 'u' in self.request.cookies:
            facebook.load_signed_request(self.request.cookies.get('u'))

        # try to load or create a user object
        if facebook.user_id:
            user = FBUser.get_by_key_name(facebook.user_id)
            if user:
                # update stored access_token
                if facebook.access_token and \
                        facebook.access_token != user.access_token:
                    user.access_token = facebook.access_token
                    user.put()
                # refresh data if we failed in doing so after a realtime ping
                if user.dirty:
                    user.refresh_data()
                # restore stored access_token if necessary
                if not facebook.access_token:
                    facebook.access_token = user.access_token

            if not user and facebook.access_token:
                me = facebook.api('/me', {'fields': _USER_FIELDS})
                try:
                    friends = [user['id'] for user in me['friends']['data']]
                    user = FBUser(key_name=facebook.user_id,
                        id=facebook.user_id, friends=friends,
                        access_token=facebook.access_token, name=me['name'],
                        email=me.get('email'), picture=me['picture'])
                    user.put()
                except KeyError, ex:
                    pass # ignore if can't get the minimum fields

        self.facebook = facebook
        self.user = user

    def init_csrf(self):
        """Issue and handle CSRF token as necessary"""
        self.csrf_token = self.request.cookies.get('c')
        if not self.csrf_token:
            self.csrf_token = str(uuid4())[:8]
            self.set_cookie('c', self.csrf_token)
        if self.request.method == 'POST' and self.csrf_protect and \
                self.csrf_token != self.request.get('_csrf_token'):
            raise CsrfException('Missing or invalid CSRF token.')

    def set_message(self, **obj):
        """Simple message support"""
        self.set_cookie('m', base64.b64encode(json.dumps(obj)) if obj else None)

    def get_message(self):
        """Get and clear the current message"""
        message = self.request.cookies.get('m')
        if message:
            self.set_message() # clear the current cookie
            return json.loads(base64.b64decode(message))

class Facebook(object):
    """Wraps the Facebook specific logic"""
    def __init__(self, app_id=facebookconf.FACEBOOK_APP_ID,
            app_secret=facebookconf.FACEBOOK_APP_SECRET):
        self.app_id = app_id
        self.app_secret = app_secret
        self.user_id = None
        self.access_token = None
        self.signed_request = {}

    def api(self, path, params=None, method='GET', domain='graph'):
        """Make API calls"""
        if not params:
            params = {}
        params['method'] = method
        if 'access_token' not in params and self.access_token:
            params['access_token'] = self.access_token
        result = json.loads(urlfetch.fetch(
            url='https://' + domain + '.facebook.com' + path,
            payload=urllib.urlencode(params),
            method=urlfetch.POST,
            headers={
                'Content-Type': 'application/x-www-form-urlencoded'})
            .content)
        if isinstance(result, dict) and 'error' in result:
            raise FacebookApiError(result)
        return result

    def load_signed_request(self, signed_request):
        """Load the user state from a signed_request value"""
        try:
            sig, payload = signed_request.split('.', 1)
            sig = self.base64_url_decode(sig)
            data = json.loads(self.base64_url_decode(payload))

            expected_sig = hmac.new(
                self.app_secret, msg=payload, digestmod=hashlib.sha256).digest()

            # allow the signed_request to function for upto 1 day
            if sig == expected_sig and \
                    data['issued_at'] > (time.time() - 86400):
                self.signed_request = data
                self.user_id = data.get('user_id')
                self.access_token = data.get('oauth_token')
        except ValueError, ex:
            pass # ignore if can't split on dot

    @property
    def user_cookie(self):
        """Generate a signed_request value based on current state"""
        if not self.user_id:
            return
        payload = self.base64_url_encode(json.dumps({
            'user_id': self.user_id,
            'issued_at': str(int(time.time())),
        }))
        sig = self.base64_url_encode(hmac.new(
            self.app_secret, msg=payload, digestmod=hashlib.sha256).digest())
        return sig + '.' + payload

    @staticmethod
    def base64_url_decode(data):
        data = data.encode('ascii')
        data += '=' * (4 - (len(data) % 4))
        return base64.urlsafe_b64decode(data)

    @staticmethod
    def base64_url_encode(data):
        return base64.urlsafe_b64encode(data).rstrip('=')

1 个答案:

答案 0 :(得分:0)

解决方案:避免使用javascript,避免使用Cookie并使用服务器端OAuth 2.0,并且可以更轻松地关注正在发生的事情并且这样做有效:

class FBUser(db.Model):
    id = db.StringProperty(required=True)
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)
    name = db.StringProperty(required=True)
    profile_url = db.StringProperty()
    access_token = db.StringProperty(required=True)
    name = db.StringProperty(required=True)
    picture = db.StringProperty()
    email = db.StringProperty()
    friends = db.StringListProperty()
    dirty = db.BooleanProperty()

class I18NPage(I18NHandler):
    def get(self):
    if self.request.get('code'):
          args = dict(
            code = self.request.get('code'),
            client_id = facebookconf.FACEBOOK_APP_ID,
            client_secret = facebookconf.FACEBOOK_APP_SECRET,
            redirect_uri = 'http://www.koolbusiness.com/',
          )
      logging.debug("client_id"+str(args))
          file = urllib.urlopen("https://graph.facebook.com/oauth/access_token?" + urllib.urlencode(args))
          try:
        logging.debug("reading file")
            token_response = file.read()
        logging.debug("read file"+str(token_response))
          finally:
            file.close()
          access_token = cgi.parse_qs(token_response)["access_token"][-1]
          graph = main.GraphAPI(access_token)
          user = graph.get_object("me")   #write the access_token to the datastore
      fbuser = main.FBUser.get_by_key_name(user["id"])
          logging.debug("fbuser "+str(fbuser))

          if not fbuser:
            fbuser = main.FBUser(key_name=str(user["id"]),
                                id=str(user["id"]),
                                name=user["name"],
                                profile_url=user["link"],
                                access_token=access_token)
            fbuser.put()
          elif fbuser.access_token != access_token:
            fbuser.access_token = access_token
            fbuser.put()