【问题标题】:Authenticating via curl with Tasty Pie, using Session Authentication使用会话身份验证通过 curl 和 Tasty Pie 进行身份验证
【发布时间】:2013-12-29 11:24:15
【问题描述】:

所以,我似乎可以从浏览器中很好地执行此操作,但我似乎无法通过 CURL 复制它。任何关于这应该如何工作的指针都非常感谢。

我执行此请求以登录用户:

curl -X POST -H "Content-Type: application/json" \ 
-d '{"username":"tester", "password":"password"}' --verbose \
http://localhost:8000/api/user/login/

并且响应似乎表明请求成功:

* About to connect() to localhost port 8000 (#0)
*   Trying 127.0.0.1... connected
> POST /api/user/login/ HTTP/1.1
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: localhost:8000
> Accept: */*
> Content-Type: application/json
> Content-Length: 44
> 
* upload completely sent off: 44out of 44 bytes
< HTTP/1.1 200 OK
< Server: nginx/1.1.19
< Date: Wed, 11 Dec 2013 12:31:34 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< Vary: Accept, Cookie
< Set-Cookie: csrftoken=h4tjM6o3QyelsAvUhdqNJPinZRdJyrBz; Path=/
< Set-Cookie: sessionid=4tsny8kcl7j9x7icr6vptnq1ims89tzr; expires=Wed, 25-Dec-2013 12:31:34 GMT; httponly; Max-Age=1209600; Path=/
< 
* Connection #0 to host localhost left intact
* Closing connection #0
{"success": true, "username": "tester"}

如果我在经过身份验证的请求中仅包含 CSRF 令牌,则会收到 401。但是,如果我同时包含 CSRF 令牌和会话 ID,则会收到某种 Python 错误。例如:

curl -X GET -H "Content-Type: application/json" -H \
"X-CSRFToken: h4tjM6o3QyelsAvUhdqNJPinZRdJyrBz" --cookie \
"sessionid=4tsny8kcl7j9x7icr6vptnq1ims89tzr" --verbose \
http://localhost:8000/api/user/ | python -mjson.tool \

我从服务器返回:

{
    "error_message": "getattr(): attribute name must be string", 
    "traceback": "Traceback (most recent call last):
File \"/opt/phaidra/env/local/lib/python2.7/site-packages/tastypie/resources.py\", line 195, in wrapper\n    response = callback(request, *args, **kwargs)\n\n  
File \"/opt/phaidra/env/local/lib/python2.7/site-packages/tastypie/resources.py\", line 426, in dispatch_list\n    return self.dispatch('list', request, **kwargs)\n\n  
File \"/opt/phaidra/env/local/lib/python2.7/site-packages/tastypie/resources.py\", line 454, in dispatch\n    self.throttle_check(request)\n\n  
File \"/opt/phaidra/env/local/lib/python2.7/site-packages/tastypie/resources.py\", line 551, in throttle_check\n    identifier = self._meta.authentication.get_identifier(request)\n\n  
File \"/opt/phaidra/env/local/lib/python2.7/site-packages/tastypie/authentication.py\", line 515, in get_identifier\n    return request._authentication_backend.get_identifier(request)\n\n  
File \"/opt/phaidra/env/local/lib/python2.7/site-packages/tastypie/authentication.py\", line 283, in get_identifier\n    return getattr(request.user, username_field)\n\n
TypeError: getattr(): attribute name must be string\n"
}

查看错误的线条并不是特别有启发性。由于除非使用 --cookie 否则不会发生此错误,因此我假设它尝试错误地解析 cookie 参数。

还应该注意的是,我使用的是 Neo4django,我认为这使我无法使用 API 密钥身份验证。我的用户的代码是这样的:

class UserResource(ModelResource):
    class Meta:
        queryset = AppUser.objects.all()
        resource_name = 'user'
        fields = ['first_name', 'last_name', 'username', 'email', 'is_staff']
        allowed_methods = ['get', 'post', 'patch']
        always_return_data = True
        authentication = MultiAuthentication(SessionAuthentication(), BasicAuthentication()) 
        authorization = Authorization()

    def prepend_urls(self):
        params = (self._meta.resource_name, trailing_slash())
        return [
            url(r"^(?P<resource_name>%s)/login%s$" % params, self.wrap_view('login'), name="api_login"),
            url(r"^(?P<resource_name>%s)/logout%s$" % params, self.wrap_view('logout'), name="api_logout")
    ]

    def login(self, request, **kwargs):
        """
        Authenticate a user, create a CSRF token for them, and return the user object as JSON.
        """
        self.method_check(request, allowed=['post'])

        data = self.deserialize(request, request.raw_post_data, format=request.META.get('CONTENT_TYPE', 'application/json'))

        username = data.get('username', '')
        password = data.get('password', '')

        if username == '' or password == '':
            return self.create_response(request, {
                'success': False,
                'error_message': 'Missing username or password'
            })

        user = authenticate(username=username, password=password)

        if user:
            if user.is_active:
                login(request, user)
                response = self.create_response(request, {
                    'success': True,
                    'username': user.username
                })
                response.set_cookie("csrftoken", get_new_csrf_key())
                return response
            else:
                return self.create_response(request, {
                    'success': False,
                    'reason': 'disabled',
                }, HttpForbidden)
        else:
            return self.create_response(request, {
                'success': False,
                'error_message': 'Incorrect username or password'
            })

def read_list(self, object_list, bundle):
        """
        Allow the endpoint for the User Resource to display only the logged in user's information
        """
        self.is_authenticated(request)
        return object_list.filter(pk=bundle.request.user.id)

(如果需要,您可以在https://github.com/OpenPhilology/phaidra/blob/master/api/api.py查看文件的全部内容)

所以,总而言之,我的主要问题/困惑点:

  1. 必须通过 curl 请求发送哪些数据才能发送经过身份验证的 GET/POST/等?
  2. 用户资源的身份验证值是否正确?
  3. 我应该能够仅使用 CSRF 令牌进行身份验证,还是会话 ID 也是必需的?

提前感谢您对此的任何见解!

编辑: 这是我们拥有的自定义用户模型。

from django.contrib.auth import authenticate

from django.db import models as django_models 
from neo4django.db import models
from neo4django.graph_auth.models import User, UserManager


class AppUser(User):
    objects = UserManager()

    USERNAME_FIELD = 'username'

    def __unicode__(self):
        return unicode(self.username) or u'' 

【问题讨论】:

    标签: python authentication curl tastypie neo4django


    【解决方案1】:

    这里的问题最终有两个方面:

    我发现 Django 函数 get_user_model() 失败了——它在多个地方被使用——但 不是,因为 USERNAME_FIELD 是空白的。如果我将这些值硬编码到这个文件中,一切正常。相反,问题在于它失败了,因为 Django 需要一个非常具体的自定义用户模型命名方案。来自 Django 文档:

    这个点对描述了 Django 应用程序的名称(必须是 在您的 INSTALLED_APPS 中),以及您使用的 Django 模型的名称 希望用作您的用户模型。

    https://docs.djangoproject.com/en/dev/topics/auth/customizing/#substituting-a-custom-user-model

    然而,这并不是故事的全部。 Django 假定您的 AUTH_USER_MODEL 可以被中间的句点分割,这将给它两个变量,“app_label”和“model_name”。见:

    def get_user_model():
        "Return the User model that is active in this project"
        from django.conf import settings
        from django.db.models import get_model
    
        try:
            app_label, model_name = settings.AUTH_USER_MODEL.split('.')
        except ValueError:
            raise ImproperlyConfigured("AUTH_USER_MODEL must be of the form 'app_label.model_name'")
        user_model = get_model(app_label, model_name)
        if user_model is None:
            raise ImproperlyConfigured("AUTH_USER_MODEL refers to model '%s' that has not been installed" % settings.AUTH_USER_MODEL)
        return user_model
    
    (in file: django/contrib/auth/__init__.py)
    

    但是,我的可以通过“从 core.models.user 导入 AppUser”访问。我不得不扁平化我的项目结构,所以我有一个名为“app”的应用程序,我的所有模型都在一个名为“models.py”的文件中,然后在 settings.py 中我能够将我的 AUTH_USER_MODEL 设置为“app.AppUser”。

    奇怪的是:在许多其他情况下,我能够通过 API 登录,即使我的 APP_USER_MODEL 设置为 'core.models.user.AppUser'。只有当我尝试使用 SessionAuth 时才遇到问题。

    此外,最近对 Neo4Django 的一些更改也必须升级,因为它们直接处理图形身份验证。以前,backends.py 不是属性导入和尝试使用我的自定义模型。现在确实如此。具体来说,这个文件:

    https://github.com/scholrly/neo4django/blob/9058c0b6f4eb9d23c2a87044f0661f8178b80b12/neo4django/graph_auth/backends.py

    【讨论】:

      【解决方案2】:

      您使用的是哪个版本的 Django?如果您使用的是 1.5 或更高版本,您可能会遇到未指定用户名字段名称的问题:

      https://github.com/toastdriven/django-tastypie/blob/master/tastypie/compat.py

      【讨论】:

      • 您如何建议我检查这是否是错误?将 Django 升级到最新版本?我正在使用 Django 1.5.3。谢谢!
      • 看起来您正在使用自定义用户模型并且您没有设置 USERNAME_FIELD docs.djangoproject.com/en/dev/topics/auth/customizing/…
      • 我已经更新了原始帖子以包含更新后的用户模型和 USERNAME_FIELD 常量,但它似乎没有效果?
      • 另外,当我从浏览器访问 API 时,仅通过 curl 访问 API 时,难道不应该告诉我没有收到此问题吗?我认为模型问题也会影响 API 浏览器端。
      • 我很难过。我会使用 pdb 在堆栈跟踪的最后一行插入以尝试诊断正在发生的事情。一定要检查 request.user 是否已经设置。如果是,则 sessionid 有效。我假设 username_field 为 None,但原因有点神秘。
      猜你喜欢
      • 2021-10-11
      • 2017-04-07
      • 1970-01-01
      • 2017-09-18
      • 2017-06-21
      • 2014-10-30
      • 2012-03-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多