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

Joa*_*imB 33 django cors angularjs 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用于经过身份验证的用户.如果我尝试重新登录它将失败,因为它出现了"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)
Run Code Online (Sandbox Code Playgroud)

客户端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 = '';
    });
};
Run Code Online (Sandbox Code Playgroud)

}]);

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

Vis*_*per 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.例如,请参阅如何从一个Django项目运行多个网站

Django API应用程序 - 为了使CORS和CSRF保护工作,我需要在API后端执行以下操作.

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

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

MIDDLEWARE_CLASSES = (
    ...
    'django.middleware.csrf.CsrfViewMiddleware',
    ...
    'corsheaders.middleware.CorsMiddleware',
)
Run Code Online (Sandbox Code Playgroud)

另请参阅GitHub上的Django CORS头文件

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

CORS_ALLOW_CREDENTIALS =真

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

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def myResource(request):
    ...
Run Code Online (Sandbox Code Playgroud)

用于AngularJS的Django App - 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')
Run Code Online (Sandbox Code Playgroud)

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

$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
$httpProvider.defaults.withCredentials = true;
Run Code Online (Sandbox Code Playgroud)

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

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

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

另请参阅ngCookies模块.

使用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
    }
});
Run Code Online (Sandbox Code Playgroud)

我希望这有帮助!!

  • 这里的第一步是安装Django CORS Headers扩展:`pip install django-cors-headers` (2认同)

Der*_*wok 15

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

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

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


Joa*_*imB 7

所以我找到了自己的解决方案,似乎工作得很好.

这是我的代码的新片段:

后端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)
Run Code Online (Sandbox Code Playgroud)

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

$http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
Run Code Online (Sandbox Code Playgroud)

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

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

CORS_ALLOW_HEADERS = (
'x-requested-with',
'content-type',
'accept',
'origin',
'authorization',
'X-CSRFToken'
Run Code Online (Sandbox Code Playgroud)

)

而已!


Joa*_*imB 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;
Run Code Online (Sandbox Code Playgroud)

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

希望这有助于某人!