gpt4 book ai didi

python - Facebook Canvas 应用是否需要签名请求?

转载 作者:太空宇宙 更新时间:2023-11-03 11:34:28 30 4
gpt4 key购买 nike

我看到 OAuth 也被推荐用于 facebook canvas 应用程序并且我没有工作的代码,它尝试使用 signed_request 但每次我更改 session 我都必须重新加载:

class Facebook(object):
"""Wraps the Facebook specific logic"""
def __init__(self, app_id=conf.FACEBOOK_APP_ID,
app_secret=conf.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=u'GET', domain=u'graph'):
"""Make API calls"""
if not params:
params = {}
params[u'method'] = method
if u'access_token' not in params and self.access_token:
params[u'access_token'] = self.access_token
result = json.loads(urlfetch.fetch(
url=u'https://' + domain + u'.facebook.com' + path,
payload=urllib.urlencode(params),
method=urlfetch.POST,
headers={
u'Content-Type': u'application/x-www-form-urlencoded'})
.content)
if isinstance(result, dict) and u'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(u'.', 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[u'issued_at'] > (time.time() - 86400):
self.signed_request = data
self.user_id = data.get(u'user_id')
self.access_token = data.get(u'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({
u'user_id': self.user_id,
u'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(u'ascii')
data += '=' * (4 - (len(data) % 4))
return base64.urlsafe_b64decode(data)

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


class CsrfException(Exception):
pass


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

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[u'P3P'] = u'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(u'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__) +
u': \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):
"""Set a cookie"""
if value is None:
value = 'deleted'
expires = datetime.timedelta(minutes=-50000)
jar = Cookie.SimpleCookie()
jar[name] = value
jar[name]['path'] = u'/'
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(u': ', 1))

def render(self, name, **data):
"""Render a template"""
if not data:
data = {}
data[u'js_conf'] = json.dumps({
u'appId': conf.FACEBOOK_APP_ID,
u'canvasName': conf.FACEBOOK_CANVAS_NAME,
u'userIdOnServer': self.user.user_id if self.user else None,
})
data[u'logged_in_user'] = self.user
data[u'message'] = self.get_message()
data[u'csrf_token'] = self.csrf_token
data[u'canvas_name'] = conf.FACEBOOK_CANVAS_NAME
self.response.out.write(template.render(
os.path.join(
os.path.dirname(__file__), 'templates', name + '.html'),
data))

def init_facebook(self):
"""Sets up the request specific Facebook and User instance"""
facebook = Facebook()
user = None

# initial facebook request comes in as a POST with a signed_request
if u'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 = u'GET'
self.set_cookie(
'u', 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 = User.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(u'/me', {u'fields': _USER_FIELDS})
try:
friends = [user[u'id'] for user in me[u'friends'][u'data']]
user = User(key_name=facebook.user_id,
user_id=facebook.user_id, friends=friends,
access_token=facebook.access_token, name=me[u'name'],
email=me.get(u'email'), picture=me[u'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(u'c')
if not self.csrf_token:
self.csrf_token = str(uuid4())[:8]
self.set_cookie('c', self.csrf_token)
if self.request.method == u'POST' and self.csrf_protect and \
self.csrf_token != self.request.POST.get(u'_csrf_token'):
raise CsrfException(u'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(u'm')
if message:
self.set_message() # clear the current cookie
return json.loads(base64.b64decode(message))

我将上面的代码更改为 Canvas 应用程序的 OAuth 服务器端,然后我可以让应用程序按照我想要的方式运行。但是如果我使用 OAuth 2.0,我真的需要 signed_request 吗?如果我使用 OAuth,则 signed_request 似乎是不必要的,而 OAuth 可以做到这一切。我对函数 init_facebook 做了相当多的更改:

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:
fbdata= parse_signed_request(self.request.get('signed_request'), facebookconf.FACEBOOK_APP_SECRET)

facebook.signed_request = fbdata
facebook.user_id = fbdata.get('user_id')
facebook.access_token = fbdata.get('oauth_token')

if facebook.user_id:
graph = GraphAPI(facebook.access_token)
user = graph.get_object("me") #write the access_token to the datastore
fbuser = FBUser.get_by_key_name(user["id"])
#logging.debug("fbuser "+fbuser.name)
self.user = fbuser
if not fbuser:
fbuser = FBUser(key_name=str(user["id"]),
id=str(user["id"]),
name=user["name"],
profile_url=user["link"],
access_token=facebook.access_token)
fbuser.put()
elif fbuser.access_token != facebook.access_token:
fbuser.access_token = facebook.access_token
fbuser.put()

# try to load or create a user object
if facebook.user_id:
logging.debug("loading 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

现在它使用 OAuth,而不是将方法从 POST 更改为 GET,我一直不明白为什么它必须这样做。我有更多代码,但也许您已经知道得足够多了,可以告诉我我是否做错了并且应该回到更基本的示例。

例如,我在注销用户时遇到了一些问题,我不得不编写一个自定义注销处理程序:

class FBLogoutHandler(webapp2.RequestHandler):
def get(self):
logging.debug('in fblogout')
current_user = main.get_user_from_cookie(self.request.cookies, facebookconf.FACEBOOK_APP_ID, facebookconf.FACEBOOK_APP_SECRET)
if current_user:
graph = main.GraphAPI(current_user["access_token"])
profile = graph.get_object("me")
accessed_token = current_user["access_token"]
logging.debug('setting cookie')
self.set_cookie("fbsr_" + facebookconf.FACEBOOK_APP_ID, None, expires=time.time() - 86400)
logging.debug('redirecting with token '+str(accessed_token))
self.redirect('https://www.facebook.com/logout.php?next=http://www.facebook.com&access_token=%s' % accessed_token)
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 get_host(self):
return os.environ.get('HTTP_HOST', os.environ['SERVER_NAME'])

这个解决方案可能不是最好的,所以我想知道我的 Canvas 应用程序有哪些替代方案?

谢谢

最佳答案

signed_request 始终在您访问 Canvas 应用程序时更新(刷新父框架时),因此这是为用户获取最新的 access_token 的好方法(通常只持续一个小时)。

使用oauth获得的access_token有相同的过期时间,所以如果你完全依赖oauth,你将不得不每小时验证一次用户。

我倾向于结合使用两者。在初次使用该应用程序时,我使用 oauth 获取 access_token 并访问 API。然后,在随后的访问中,我依靠 signed_request 获取 access_token 并自动为用户个性化内容(因为它告诉我他们是谁并允许我访问API 而无需再次遵循 OAuth 流程)。

关于python - Facebook Canvas 应用是否需要签名请求?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8375283/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com