AngularJS + Django Rest Framework + CORS(CSRF Coo​​kie未显示在客户端)

时间:2013-07-29 18:09:30

标签: django angularjs cors django-rest-framework django-cors-headers

我正在使用和Django Rest Framework + Django CORS Headers在AngularJS中开发一个1页的应用程序。

我的问题是" csrftoken"当我联系后端时,cookie永远不会出现在我的浏览器中。

例如:我正在使用帖子进行登录。我得到了" sessionid" cookie正确,但" csrftoken"从来没有出现过,因此我无法从我的客户那里做适当的帖子,因为我因为缺少csrf令牌而被拒绝。

  • 我已经分析了API的响应标头,而csrftoken则不是。
  • 我已经直接查看了其余的API浏览器,它显示在那里。
  • 只是要指出,我可以执行我的第一个POST登录,因为Django Rest Framework仅强制CSRF用于经过身份验证的用户。如果我尝试重新登录它将会失败,因为#34; sessionid" -cookie它会出现。
  • 我没有绕过CSRF保护,因为stackoverflow上的一些帖子表明。

前/后端的一些代码段。这些是未完成的片段,所以不要挂在写得不好的代码上。

后端API LoginView

class LoginView(APIView):

renderer_classes = (JSONPRenderer, JSONRenderer)

def post(self, request, format=None):
    serializer = LoginSerializer(data=request.DATA)

    if serializer.is_valid():
        userAuth = authenticate(username=serializer.data['username'], password=serializer.data['password'])

        if userAuth:

            if userAuth.is_active:
                login(request, userAuth)

                loggedInUser = AuthUserProfile.objects.get(pk=1)
                serializer = UserProfileSerializer(loggedInUser)

                user = [serializer.data, {'isLogged': True}]



        else:
            user = {'isLogged': False}

        return Response(user, status=status.HTTP_200_OK)

    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

客户端AngularJS登录控制器

.controller('LoginCtrl', ['$scope', '$http', 'uService', '$rootScope', function(scope, $http, User, rootScope) {

scope.login = function() {

    var config = {
        method: 'POST',
        withCredentials: true,
        url: rootScope.apiURL+'/user/login/',
        data : scope.loginForm
    };

    $http(config)
    .success(function(data, status, headers, config) {

        if (status == 200) {
            console.log(data[0]); //Test code
            // succefull login
            User.isLogged = true;
            User.username = data.username;

        }
        else {
            console.log(data); //Test code
            User.isLogged = false;
            User.username = '';
        }

    })
    .error(function(data, status, headers, config) {
        console.log('Testing console error');
        User.isLogged = false;
        User.username = '';
    });
};

}]);

任何有任何好的提示/想法/例子的人?

5 个答案:

答案 0 :(得分:35)

子域A上的AngularJS单页Web应用程序,使用CORS和CSRF保护与子域B上的Django JSON(REST)API进行通信

由于我目前正在进行类似的设置,并且正在努力让CORS与CSRF保护一起正常工作,我想在这里分享我自己的经验。

设置 - SPA和API都位于同一域的不同子域中:

  • AngularJS(1.2.14)子域app.mydomain.com上的单页Web应用程序
  • Django App(1.6.2)在子域api.mydomain.com上实现JSON REST API

AngularJS应用程序通过Django App在与Django API APP相同的项目中提供,以便设置CSRF Coo​​kie。例如,请参阅How to run multiple websites from one Django project

Django API App - 为了使CORS和CSRF保护工作,我需要在API后端执行以下操作。

在此应用的settings.py中(Django项目settings.py的扩展名):

  • 添加corsheaders应用和中间件以及CSRF中间件:
INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

MIDDLEWARE_CLASSES = (
    ...
    'django.middleware.csrf.CsrfViewMiddleware',
    ...
    'corsheaders.middleware.CorsMiddleware',
)

另见Django CORS headers on GitHub

  • 将SPA Webapp的域添加到CORS_ORIGIN_WHITELIST
CORS_ORIGIN_WHITELIST = [
    ...
    'app.mydomain.com',
    ...
]
  • 将CORS_ALLOW_CREDENTIALS设置为True。这一点很重要,如果您不这样做,将不会随请求一起发送CSRF cookie
  

CORS_ALLOW_CREDENTIALS = True

将ensure_csrf_cookie装饰器添加到处理JSON API请求的视图中:

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def myResource(request):
    ...

用于AngularJS的Django应用程序 - AngularJS应用程序通过同一项目中的Django App提供。这个Django应用程序设置为设置CSRF Coo​​kie。然后,来自cookie的CSRF令牌用于对API的请求(因此作为同一Django项目的一部分运行)。

请注意,几乎所有与AngularJS应用程序相关的文件都只是Django透视图中的静态文件。 Django App只需要提供index.html来设置cookie。

在此应用的settings.py中(同样是Django项目settings.py的扩展名),设置CSRF_COOKIE_DOMAIN,以便子域也可以使用它们:

  

CSRF_COOKIE_DOMAIN =" .mydomain.com"

在views.py中,我只需要再次使用ensure_csrf_cookie装饰器渲染AngularJS index.html文件:

from django.shortcuts import render
from django.views.decorators.csrf import ensure_csrf_cookie

# Create your views here.
@ensure_csrf_cookie
def index(request):
    return render(request, 'index.html')

使用AngularJS向API发送请求 - 在AngularJS App配置中设置以下$ httpProvider默认值:

$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
$httpProvider.defaults.withCredentials = true;

再次注意withCredentials,这可以确保在请求中使用CSRF Coo​​kie。

下面我将展示如何使用AngularJS $ http服务和JQuery向api发出请求:

$http.post("http://api.mydomain.com/myresource", {
    field1   : ...,
      ...
    fieldN   : ...
}, {
    headers : {
        "x-csrftoken" : $cookies.csrftoken
    }
});

另见ngCookies module

使用JQuery(1.11.0):

$.ajax("http://api.mydomain.com/myresource", {
    type: 'POST',
    dataType : 'json',
    beforeSend : function(jqXHR, settings) {
        jqXHR.setRequestHeader("x-csrftoken", get_the_csrf_token_from_cookie());
    },
    cache : false,
    contentType   : "application/json; charset=UTF-8",
    data : JSON.stringify({
        field1   : ...,
          ...
        fieldN   : ...
    }),
    xhrFields: {
        withCredentials: true
    }
});

我希望这会有所帮助!!

答案 1 :(得分:15)

直接来自文档https://docs.djangoproject.com/en/1.9/ref/csrf/#ajax

  

如果您的视图未呈现包含csrf_token的模板   模板标签,Django可能不会设置CSRF令牌cookie。这是   在表单动态添加到页面的情况下很常见。至   解决这种情况,Django提供了一个强制的视图装饰器   设置cookie:ensure_csrf_cookie()。

由于您的应用程序是单页面应用程序,因此您可以将ensure_csrf_cookie()添加到负责初始页面加载的视图中。

答案 2 :(得分:7)

所以我找到了自己的解决方案,似乎效果很好。

这是我的代码的新片段:

Backend API LoginView(添加了一个装饰器,强制将csrf标记添加到正文中)

class LoginView(APIView):

renderer_classes = (JSONPRenderer, JSONRenderer)

@method_decorator(ensure_csrf_cookie)
def post(self, request, format=None):
    c = {}
    c.update(csrf(request))
    serializer = LoginSerializer(data=request.DATA)

    if serializer.is_valid():
        userAuth = authenticate(username=serializer.data['username'], password=serializer.data['password'])

        if userAuth:

            if userAuth.is_active:
                login(request, userAuth)

                loggedInUser = AuthUserProfile.objects.get(pk=1)
                serializer = UserProfileSerializer(loggedInUser)

                user = [serializer.data, {'isLogged': True}]



        else:
            user = {'isLogged': False}

        return Response(user, status=status.HTTP_200_OK)

    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

AngularJS客户端(将标记添加到请求标头)

$http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;

服务器端设置文件(特别是django-cors-headers)

默认情况下会添加前5个,但您需要添加“X-CSRFToken”以允许使用CORS从客户端到API的此类标头,否则将拒绝该帖子。

CORS_ALLOW_HEADERS = (
'x-requested-with',
'content-type',
'accept',
'origin',
'authorization',
'X-CSRFToken'

多数民众赞成!

答案 3 :(得分:6)

此解决方案的一个小更新。

从AngularJS 1.2.10开始,您需要为客户端中的每个请求类型设置CSRF cookie:

$http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
$http.defaults.headers.put['X-CSRFToken'] = $cookies.csrftoken;
$http.defaults.headers['delete']['X-CSRFToken'] = $cookies.csrftoken;

这是由于1.2.9和1.2.10之间发生的以下变化 https://github.com/cironunes/angular.js/commit/781287473bc2e8ee67078c05b76242124dd43376

希望这有助于某人!

答案 4 :(得分:2)

经过如此多的搜索后,我登陆了这个解决方案,它的工作形成了我在本地系统和实时网络派系服务器上这是我对 Django 用户的解决方案请转到位于项目中的apache文件夹然后在bin中找到

httpd.conf或您的服务器配置为php或其他用户(通常位于* .conf文件,如httpd.conf或apache.conf),或在.htaccess中。然后只需添加此代码

<IfModule mod_headers.c>
SetEnvIf Origin (.*) AccessControlAllowOrigin=$1
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header set Access-Control-Allow-Credentials true
</IfModule> 

然后在 angular js app 中,你只需要放置

angular.module('app', ['ngCookies'])
    .config([
   '$httpProvider',
   '$interpolateProvider',
   function($httpProvider, $interpolateProvider, $scope, $http) {
       $httpProvider.defaults.withCredentials = true;
       $httpProvider.defaults.xsrfCookieName = 'csrftoken';
       $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
   }]).
   run([
   '$http',
   '$cookies',
   function($http, $cookies) {
       $http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
   }]);

它在Django Angularjs平台上为我工作。

https://gist.github.com/mlynch/be92735ce4c547bd45f6