Django-Rest-Framework sessionid和csrftoken未在Chrome上设置

时间:2014-08-09 22:10:54

标签: django google-chrome session django-rest-framework csrf-protection

我有一个简单的Django(Rest Framework)应用程序。我已经正确启用了CSRF,CORS,Session中间件。我尝试调试使用Backbone编写的前端UI,并且sessionid和csrftoken不在浏览器的持久存储中。 为了让我更加困惑,当我退出时,我会收到匿名用户的sessionid(没有对csrftoken),并且该cookie会被持久化。

我使用谷歌浏览器。症状:

  • 当我执行登录时,我在响应中收到两个令牌的Set-Cookie标头
  • 令牌有不同的到期日期
  • 代币出现在Chrome的响应Cookie标签中,但不会出现在Cookie存储中
  • 如果我退出,我收到匿名用户的sessionid,没有csrftoken,并且这是作为cookie保留的。

我只是在Pycharm和Chrome的帮助下尝试在127.0.0.1:63342上进行调试。

有效设置摘要:

应用程序定义

INSTALLED_APPS = (
    # 'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.sessions',
    'django.contrib.contenttypes',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'corsheaders',
    'south',

    'tenant',
    'agriculture',
)

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
        # 'rest_framework.authentication.BasicAuthentication',
    ),
    'PAGINATE_BY': 10,
    'PAGINATE_BY_PARAM': 'page_size',
}

SESSION_COOKIE_DOMAIN = CSRF_COOKIE_DOMAIN = None
SESSION_COOKIE_AGE = 60 * 60 * 24 * 30

# CORS headers settings
CORS_ORIGIN_WHITELIST = (
    'localhost:63342',          # List here all the white-listed access points for the API
    '127.0.0.1:63342',
    ...,
)
CORS_ALLOW_CREDENTIALS = True

相关观点:

class LoginView(APIView):
    """
    The view will respond to the login request by using the underlying Django session authentication. In addition to the
    default behavior will return rich information about the current user being logged in.
    """
    serializer_class = serializers.UserSerializer

    def post(self, request, *args, **kwargs):
        # Get the parameters from the request
        username = request.DATA['username']
        password = request.DATA['password']
        remember = request.DATA.get('remember', False)
        logger.debug('Attempt authentication with %s : "%s"' % (username, password,))
        # Attempt authentication
        user = authenticate(username=username, password=password)
        if user is not None:
            if user.is_active:
                # Care for the session
                login(request, user)
                # se the expiration to 0 if remember wasn't requested
                if not remember:
                    request.session.set_expiry(0)
                # Return successful response
                logger.debug('Login successfully')
                return Response(self.serializer_class(user).data)
            else:
                logger.warn('User %s is de-activated' % username)
                return Response(status=status.HTTP_403_FORBIDDEN)
        else:
            logger.debug('Unauthorized access with %s : "%s"' % (username, password,))
            return Response(status=status.HTTP_401_UNAUTHORIZED)


class AuthenticateView(APIView):
    """
    Based on the received session token, we will check if the session is still valid, meaning that we will check if the
    user is authenticated. If the request gets to be processed, means that the session token is still valid, otherwise
    we will issue an 401 status. If the session is valid, then return the user data.
    """
    permission_classes = (IsAuthenticated,)
    serializer_class = serializers.UserSerializer

    def get(self, request, *args, **kwargs):
        return Response(self.serializer_class(request.user).data)


class LogoutView(APIView):
    """
    Will simply care to logout the user which was logged in. Will use the default behavior form Django, which doesn't
    require that the uses is logged in.
    """
    def post(self, request, *args, **kwargs):
        logout(request)
        return Response(status=status.HTTP_200_OK)

1 个答案:

答案 0 :(得分:3)

好的,我已经确定了这个问题。

它与如何在服务器上配置Ajax调用有关。我只是在成功调用身份验证服务之后才尝试设置它们,这应该是在应用程序初始化时第一次完成的。

因此,后端的代码是正确的,并按预期运行。来自前端应用程序的更正行为是使用服务组件实现的,该组件负责Ajax初始化;我用Backbone和RequireJS运行它。

ajaxSetup.js服务:

define([
    'jquery',
    'const'
], function ($, Const) {
    "use strict";

    // these methods don't need csrf header
    var isCsrfSafeMethod = /^(GET|HEAD|OPTIONS|TRACE)$/;

    var setupAjax = function () {
        // Setup the AJAX calls
        $.ajaxSetup({
            // enable authentication
            xhrFields: { withCredentials: true },

            // setup csrf handling
            beforeSend: function (xhr, settings) {
                if (!isCsrfSafeMethod.test(settings.type) &&
                    $.cookie(Const.CSRF_COOKIE_NAME)) {

                    xhr.setRequestHeader("X-CSRFToken", $.cookie(Const.CSRF_COOKIE_NAME));

                }
            },

            // This will setup an handler for the errors 401, redirecting us to an internal login route.
            statusCode: {
                401: function () {
                    console.log("Unauthorized access, trying to re-direct to login.");
                    // Redirect the to the login page.
                    window.location.replace("#/login");
                }
            }
        });
    };

    return { setupAjax: setupAjax };
});

显然,我有一个模块Const,它暴露了CSRF_COOKIE_NAME常量(以及其他)。当单页应用程序引导时,它会产生如下内容:

desktopInit.js模块:

define([
    'jquery',
    'backbone',
    'underscore',
    'services/ajaxSetup',
    'services/authentication',
    'text!templates/desktop/body.html',
    'views/desktop/navbar',
    'bootstrap'     // load dependency to be used by views
], function ($, Backbone, _, AjaxSetup, Authentication, BodyTemplate, NavbarView) {

    console.log("Desktop initialization ...");

    // Setup the AJAX calls
    AjaxSetup.setupAjax();

    // Try to authenticate, if there is a logged in user.
    Authentication.authenticate();

    // Fill in the body element with the bare bone layout
    $("body").html(_.template(BodyTemplate, {}));

    // we instantiate here the view because we need to control the creation moment of this view, rather then to have it
    // as a singleton served by require.js. At this stage, the bare bone layout for the <body> element exist and the
    // view can find it's anchor and it can render itself properly.
    (new NavbarView()).render();

    // Start the backbone history
    Backbone.history.start();

});

这很有趣......

- 罗瓦


使用Angular进行后期编辑:有一个特殊的地方放置你的CSRF握手代码:在我的一个项目中,在主配置调用中我为$ http服务执行类似的操作:< / p>

(function () {

    angular.module('MyApp').config(['$httpProvider', function () {
        // set up CRSF handshake with Django Rest Framework
        $httpProvider.defaults.xsrfCookieName = CONST.CSRF_COOKIE_NAME;
        $httpProvider.defaults.xsrfHeaderName = CONST.CSRF_HEADER_NAME;
        // we will use credentials
        $httpProvider.defaults.withCredentials = true;
    }]);

})();