我有一个简单的Django(Rest Framework)应用程序。我已经正确启用了CSRF,CORS,Session中间件。我尝试调试使用Backbone编写的前端UI,并且sessionid和csrftoken不在浏览器的持久存储中。 为了让我更加困惑,当我退出时,我会收到匿名用户的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)
答案 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;
}]);
})();