如何从其他来源向App Engine服务发出经过身份验证的PUT请求

时间:2019-03-17 11:49:01

标签: http google-app-engine authentication cors google-authentication

我有一个实施了一些方法的App Engine服务,在其中我使用app.yaml中的login: admin选项来限制所有路由。

向我的服务发出POST请求有效:

fetch('http://localhost:8081/api/foo', {
  credentials: 'include'});

但是发出PUT请求失败

await fetch('http://localhost:8081/api/foo', {
  credentials: 'include',
  method: 'PUT',
  body: 'hi there'});

,出现以下错误:

Response to preflight request doesn't pass access control check:
Redirect is not allowed for a preflight request.

我了解这是因为我的请求未通过身份验证,并且服务器将我的请求重定向到登录页面。我不了解如何进行身份验证。

我正在使用webapp2处理请求,并设置以下标头:

self.response.headers['Access-Control-Allow-Credentials'] = 'true'
self.response.headers['Content-Type'] = 'application/json'
# This feels wrong, but I still don't clearly understand what this header's purpose is...
self.response.headers['Access-Control-Allow-Origin'] = self.request.headers['Origin']

我认为更深层的问题是我不理解该登录功能的工作原理(它是基于cookie的吗?为什么它可以与GET一起使用,但不能与PUT一起使用?...),我也不是真正理解CORS

感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

因此,在与Dan Cornilescu讨论之后,这是我想出的解决方案(谢谢Dan!)

它们继承了自定义HandlerWrapper,而不是让我的类继承webapp2.RequestHandler。 最大的区别在于,当接收到“ OPTIONS”请求(即预检)时,不需要登录。这就是造成我的问题的原因:我无法获得预检请求的身份验证,因此现在不必如此。

还可以在其中处理CORS,并提供允许的来源列表

class HandlerWrapper(webapp2.RequestHandler):
  def __init__(self, request, response):
    super(HandlerWrapper, self).__init__(request, response)

    self.allowed_origins = [
      r'http://localhost(:\d{2,})?$', # localhost on any port
      r'https://\w+-dot-myproject.appspot.com' # all services in the app engine project
    ]
    self.allowed_methods = 'GET, PUT, POST, OPTIONS'
    self.content_type = 'application/json'
    # login mode: either 'admin', 'user', or 'public'
    self.login = 'admin'

  def dispatch(self):
    # set the Allow-Origin header.
    if self.request.headers.has_key('origin') and match_origin(self.request.headers['Origin'], self.allowed_origins):
      self.response.headers['Access-Control-Allow-Origin'] = self.request.headers['Origin']

    # set other headers
    self.response.headers['Access-Control-Allow-Methods'] = self.allowed_methods
    self.response.headers['Content-Type'] = 'application/json'
    self.response.headers['Access-Control-Allow-Credentials'] = 'true'

    # Handle preflight requests: Never require a login.
    if self.request.method == "OPTIONS":
      # For some reason, the following line raises a '405 (Method Not Allowed)'
      # error, so we just skip the dispatch and it works.
      # super(HandlerWrapper, self).dispatch()
      return

    # Handle regular requests
    user = users.get_current_user()
    if self.login == 'admin' and not users.is_current_user_admin():
      self.abort(403)
    elif self.login == 'user' and not user:
      self.abort(403)
    else:
      super(HandlerWrapper, self).dispatch()

def match_origin(origin, allowed_origins):
  for pattern in allowed_origins:
    if re.match(pattern, origin): return True
  return False

答案 1 :(得分:0)

login: admin配置基于Users API,仅在第一代标准环境中可用。不是CORS问题。在handlers element表的login行中:

  

使用具有登录设置(而非可选)的URL处理程序时   匹配URL,处理程序将首先检查用户是否已登录   使用其authentication option连接到应用程序。如果没有,通过   默认情况下,用户将被重定向到登录页面。您也可以使用   auth_fail_action将应用程序配置为仅拒绝请求   用于未经过正确身份验证的用户的处理程序   将用户重定向到登录页面的过程。

要使用Users API,用户必须在发出PUT请求之前直接登录。首先发出一个GET请求,这会将您重定向到登录页面,执行登录,然后发出PUT请求。

如果那不是您可以实现的,那么您需要使用a different authentication mechanism,而不是基于login: admin的那个。

更新

以上内容是正确的,但在解决了用户API身份验证时却没有关系-您确实提到了对同一URL的其他一些请求方法也可以使用。

您得到的错误确实与CORS相关,请参阅Response to preflight request doesn't pass access control check。但是我建议不要将注意力集中在已接受的答案上(仅用于解决CORS问题),而应关注this one,这是关于正确执行CORS。