如何在Django 2.2中向Angular 8的POST请求添加CSRF令牌

5

我有一个使用Django后端和Angular前端的应用程序。

现在,它们已经连接,我可以从Django获取数据并在Angular中显示,并且可以向Django发送post请求。

但是,问题出在Django的CSRF令牌上。尽管我知道这样做不安全,但我禁用了Django中的CSRF中间件以便请求可以被处理。

以下是进行post请求的方法:

loadQuestion(id): Observable<any> {
    const body = {'choice': 'teseted with post method'};
    return this.http.post(this.baseUrl + id + '/vote', {headers: this.header, withCredentials: true, });
  }

我根据这个链接做了一些更改。

HttpClientXsrfModule.withConfig({ cookieName: 'csrftoken', headerName: 'X-CSRFToken' })

但是我遇到了这个错误。

app.module.ts: 26未捕获的类型错误:_angular_common_http__WEBPACK_IMPORTED_MODULE_3__.HttpClientXsrfModule.withConfig 不是一个函数

所以我基于这个链接进行了更改。

HttpClientXsrfModule.withOptions({ cookieName: 'csrftoken', headerName: 'X-CSRFToken' })

这是我的Django函数返回数据的方法,正如我所说,当我禁用CSRF中间件时它可以正常工作,因此我应该解决CSRF问题并将其传递给Angular请求。

def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=4)
    except (KeyError, Choice.DoesNotExist):
        # Redisplay the question voting form.
        return HttpResponse("You didn't select a choice.")
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponse(request)

我评论的中间件代码:

'django.middleware.csrf.CsrfViewMiddleware'

错误是CSRF验证失败。请求已终止。

更新

我使用了CORS Origin,这是我的Django配置

CORS_ORIGIN_ALLOW_ALL = True

CSRF_COOKIE_SECURE = False
CSRF_USE_SESSIONS = False

CORS_ORIGIN_ALLOW_ALL = True

CORS_ALLOW_HEADERS = (
    'accept',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
    'X-CSRFToken',
    'x-csrftoken',
    'X-XSRF-TOKEN',
    'XSRF-TOKEN',
    'csrfmiddlewaretoken',
    'csrftoken',
    'X-CSRF'
)

CORS_ALLOW_CREDENTIALS = True

Angular应用程序是否在与Django相同的域上提供服务?如果不是,则您确实不需要csrf,您只需检查来源(它将始终存在于跨源请求中)。 - GwynBleidD
Angular 在 http://localhost:4200/ 上,而 Django 在 http://localhost:8000/ 上。 - Nasser Ali Karimi
@GwynBleidD 我正在使用跨域,我更新了问题,请再次检查。 - Nasser Ali Karimi
3个回答

1
您可以使用csrf_exempt包装您的视图。
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def vote():
...

如果您使用的是AngularJS 1.1.3及更高版本,则只需配置$http提供程序的cookie和header名称即可。
httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';

我应该把你提到的第二段代码放在哪里? - Nasser Ali Karimi
我使用 Angular 8。 - Nasser Ali Karimi

0

所以我使用了这个叫做ngx-cookie的库,link 。 安装它,然后将以下内容添加到您的service.ts文件中。

import {CookieService} from 'ngx-cookie';

constructor(private http: HttpClient, private _cookieService: CookieService) {
  let csrf = this._cookieService.get("csrftoken");
  if (typeof(csrf) === 'undefined') {
    csrf = '';
  }
  this.httpOptions = {
    headers: new HttpHeaders({'X-CSRFToken': csrf}),
  };
}

public postCall(payload){
  return this.http.post('/yourapi/', payload, this.httpOptions);
}

说明:

let csrf = this._cookieService.get("csrftoken");

这一行代码从cookie中获取csrf令牌

 headers: new HttpHeaders({'X-CSRFToken': csrf}),

这行代码将csrf的值添加到HttpHeaders中。

this.http.post('/yourapi/', payload, this.httpOptions);

在进行POST调用时,我们使用这些标头


0
我已经在Angular 15和Django 4.0中进行了检查,现在可以获取csrf-token。以下是解决方案(已经起作用):
  1. 在服务文件中添加函数
authorizeUser(username: string, password: string, csrfmiddlewaretoken: string): Observable<any>{
  const url = 'http://localhost:8000/admin/login/?next=/admin/'
  const encodedBody = new URLSearchParams();
  encodedBody.set('username', username);
  encodedBody.set('password', password);
  encodedBody.set('csrfmiddlewaretoken', csrfmiddlewaretoken)

  return this.http.post(url, encodedBody);

}

添加组件 AdminComponent
export class AdminComponent implements OnInit, HttpInterceptor {
  xsrf: string   = <string>this.cookieExtractor.getToken()

  form: FormGroup = new FormGroup({
      username: new FormControl('', Validators.required),
      password: new FormControl('', Validators.required)
    }
  );
  submitted = false

在之前的组件中添加这个重要的功能。
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    request = request.clone({
      headers:  new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded', 'Cookie': this.xsrf }),
      withCredentials: true
    });

    return next.handle(request);
  }

所以我在编写前端和后端服务的应用程序时遇到的主要问题是,content-type 必须是 "application/x-www-form-urlencoded" 而不是 "application/json"(你可以通过在 Django-admin 区域发送请求来检查)。希望这个解决方案能帮到你!

  • 在后端设置 CSRF_COOKIE_SECURE = True
  • 在前端删除,'Cookie': this.xsrf,因为不需要添加 cookie。
- undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接