Django Angular Authentication CSRF缓存模板

时间:2013-12-18 16:36:33

标签: ajax django angularjs csrf django-csrf

我在成功登录并注销后尝试登录时收到状态码403。

客户端用Angular编写,服务器端用Django编写。

具体如下:

  1. 客户端请求url '/'使用所有必需的静态文件(angular,bootstrap,jQuery源和我定义的角度源)获取主HTML模板 <div ng-view></div>标记将插入更多模板。
  2. 通过$ location服务重定向到网址'/#/login'
  3. 来自$routeProvider的此规则会在'/#/login'被点击后执行: $routeProvider.when('/login', { templateUrl: 'login.html' });
  4. 'login.html'由django视图提供,用于登录的表单呈现给用户
  5. 用户登录成功提供正确的凭据
  6. 然后,用户退出,点击按钮,该按钮会触发'$http.get( '/logout/' );',然后重定向到网址'/#/login'
  7. 这是问题所在。当用户填写凭证表单并发送'POST'请求时,将返回403。我认为是这样,因为这个路由只是通过角度来完成的,因为已经请求了'login.html'模板,它已经被捕获并且可以在没有命中后端的情况下提供服务,但是在注销后当前拥有的CSRF cookie是陈旧的,所以这就是为什么我得到403.所以我试图删除该模板:
  8. logout: function(){
    
        var forceLoginTemplateRequest = function(){
            if( $templateCache.get('login.html') !== 'undefined'){
                $templateCache.remove('login.html');
            }
        };
        var responsePromise = $http.get(
            urls.logout
        );
        responsePromise.success(forceLoginTemplateRequest);
        return responsePromise;
    }
    

    执行此操作后,我可以在注销后始终看到客户端请求'login.html'模板,因此我认为在从后端提供该模板时我可以提供CSRF cookie:

    #urls.py
    urlpatterns = patterns(
        '',
        ...
        url(r'^$', serve_home_template),
        url(r'^login.html$', serve_login_template),
        url(r'^login/', login_view, name='login'),
        url(r'^logout/', logout_view, name='logout'),
        ...
    )
    
    #views.py
    @ensure_csrf_cookie
    def serve_login_template(request):
        return render(request, "login.html")
    
    @ensure_csrf_cookie
    def serve_home_template(request):
        return render(request, 'home.html')
    

    但它仍然不起作用,我在登出后尝试登录时收到403。我管理它的唯一方法是简单地刷新页面,以便从后端再次请求每个文件,无论是模板还是源文件,并使用它们更新CSRF cookie。

    这是我的应用程序的运行部分,用于确保每次请求都发送CSRF cookie:

    mainModule.run(['$http','$cookies', '$location', '$rootScope', 'AuthService', '$templateCache',
        function($http, $cookies, $location, $rootScope, AuthService, $templateCache) {
            $http.defaults.headers.common['X-CSRFToken'] = $cookies.csrftoken;
    
            $rootScope.$on( "$routeChangeStart", function(event, next, current) {
                if ( !(AuthService.isLoggedIn() == "true")){
                    $location.path('/login');
                }
    
            });
    }]);
    

3 个答案:

答案 0 :(得分:0)

这可能是缓存问题。尝试将never_cache装饰器添加到您的所有视图中:

from django.views.decorators.cache import never_cache

...

@ensure_csrf_cookie
@never_cache   
def serve_login_template(request):
    return render(request, "login.html")

...

答案 1 :(得分:0)

我通过在X-CSRFTOKEN事件中设置$routeChangeStart标头来解决此问题。 我并不完全知道 module.run阶段是如何工作的,但似乎当它内部定义的某个事件发生时,在该事件的处理程序体之外定义的所有内容都不会被执行。

mainModule.run(['$http','$cookies', '$location', '$rootScope', 'AuthService',
        function($http, $cookies, $location, $rootScope, AuthService) {
            $http.defaults.headers.common['X-CSRFToken'] = $cookies.csrftoken;

            $rootScope.$on( "$routeChangeStart", function(event, next, current) {

                // Added this line
                $http.defaults.headers.common['X-CSRFToken'] = $cookies.csrftoken;

                if ( !(AuthService.isLoggedIn() == "true")){
                    $location.path('/login');
                }

            });
}]);

这与从'login.html'删除$templateCache模板一起使用。

除了使用$templateCache服务在客户端删除模板外,还可以将服务器设置为提供模板并设置以下标题:

Cache-Control: no-cache, no-store, must-revalidate
Pragma       : no-cache
Expires      : 0

解决这个问题的另一种方法是简单地强制页面刷新,但是我不喜欢这种方法,因为这不是专业的单页面应用方法。 :)

答案 2 :(得分:0)

一种解决方案可能是直接从cookie中读取当前的新鲜csrftoken,然后使用javascript更新陈旧的cookie。

var fresh_token = document.cookie.match('csrftoken=([a-zA-Z0-9]{32})