繁体   English   中英

.Net Core:使用Ionic前端验证防伪令牌

[英].Net Core: Validate Anti Forgery Token with Ionic front end

我四处张望,找到了类似的解决方案,但没有与我正在研究的完全匹配的解决方案。

我们拥有一个.net核心MVC网站,该网站带有API控制器,用于处理来自我们也在开发的离子移动应用程序的请求。

在大多数情况下,将[ValidateAntiForgeryToken]添加到API控制器操作即可。 我已经经历了生成令牌,将其传递给Ionic并将其存储在请求标头中进行验证的过程。

这是我用来获取和存储令牌的代码:

static XSRF_TOKEN_KEY: string = "X-XSRF-TOKEN";
static XSRF_TOKEN_NAME_KEY: string = "X-XSRF-TOKEN-NAME";

constructor(){}

static getXsrfToken(http: HTTP) : {tokenName: string, token: string} {
    let tokenName: string = window.sessionStorage.getItem(ValidationManager.XSRF_TOKEN_NAME_KEY);
    let token: string = window.sessionStorage.getItem(ValidationManager.XSRF_TOKEN_KEY);
    if(!tokenName || !token){
        this.fetchXsrfToken(http);
        tokenName= window.sessionStorage.getItem(ValidationManager.XSRF_TOKEN_NAME_KEY);
        token = window.sessionStorage.getItem(ValidationManager.XSRF_TOKEN_KEY);
    }
    return {
        tokenName: tokenName,
        token: token
    };
}

private static setXsrfToken({ token, tokenName }: { token: string, tokenName: string }) {
    window.sessionStorage.setItem(ValidationManager.XSRF_TOKEN_KEY, token);
    window.sessionStorage.setItem(ValidationManager.XSRF_TOKEN_NAME_KEY, tokenName);
}

private static fetchXsrfToken(http: HTTP) {
    let token: string = window.sessionStorage.getItem(ValidationManager.XSRF_TOKEN_KEY);
    let tokenName: string = window.sessionStorage.getItem(ValidationManager.XSRF_TOKEN_NAME_KEY);

    if (!token || !tokenName) {
        let apiUrl: string = AppConfig.apiUrl + "/GetAntiforgeryToken";
        http.get(apiUrl, {}, {})
            .then(r => this.setXsrfToken(JSON.parse(r.data)))
            .catch(r => console.error("Could not fetch XSRFTOKEN", r));
    } else {
        this.setXsrfToken({ token: token, tokenName: tokenName });
    }
}

这是控制器中提供防伪令牌的操作:

[HttpGet]
public override IActionResult GetAntiforgeryToken()
{
    var tokens = _antiforgery.GetAndStoreTokens(HttpContext);
    return new ObjectResult(new
    {
        token = tokens.RequestToken,
        tokenName = tokens.HeaderName
    });
    }

我通过从视图的关联打字稿文件中调用此函数来设置http插件的标头:

initializeHttp() {
    let token = ValidationManager.getXsrfToken(this.http);
    this.http.setHeader(token.tokenName, token.token);
    console.log("Http Initialized: ", token);
}

那么我通过http插件发出的任何请求都会在控制器的操作中得到正确验证:

this.http.post(apiUrl, {}, {}).then(response => {
   that.navCtrl.setRoot(HomePage);
});

到目前为止,一切正常。 当我尝试使用XmlHttpRequest而不是内置的http插件进行POST时,就会出现问题:

let file = {
    name: e.srcElement.files[0].name,
    file: e.srcElement.files[0],
  };

  let formData: FormData = new FormData();
  formData.append('file', file.file);

  let xhr: XMLHttpRequest = new XMLHttpRequest();
  xhr.open('POST', apiUrl, true);
  console.log("setting request header: ", tokenVal); //verify that tokenVal is correct
  xhr.setRequestHeader("X-XSRF-TOKEN", tokenVal);
  xhr.send(formData);

如果我从控制器的操作中删除了[ValidateAntiForgeryToken]属性,则该文件将正确发布。 但是,我没有尝试过使用包含的属性。

我认为问题与Ionic自动将验证令牌添加到Cookie中有关,并且cookie与http插件的请求一起传递。 但是, XMLHttpRequest不会传递cookie(并且不能传递cookie吗?)。

在过去的几天里,我已经阅读了很多有关该主题的文章,但我承认这种验证对我来说仍然是一个黑匣子。 有没有一种方法可以仅使用在标头中传递的令牌来验证我的操作中的请求?

我遇到此问题的原因是我需要上传一个文件,而我无法使用http插件执行该操作。 有一些使用Ionic的file-transfer插件上传图像的解决方案,但已过时,建议使用XmlHttpRequest代替发行说明。

我尝试过的其他方法:

  • 我已经找到了使用System.Web.Helpers.AntiForgery在服务器上进行自定义验证的.net标准解决方案,但是该命名空间未包含在.net核心中,因此我找不到等效的名称空间。
  • 我尝试了多种使用http插件发布文件的方法(因为它没有验证antiForgery令牌的问题)。 我尝试的所有操作均导致操作被击中,但所发布的文件始终为null 使用http插件上传文件的解决方案也是可以接受的。

为什么我可以在这个问题上花整整两天的时间,但是一旦发布问题,我就找到了答案? 有时我认为互联网之神只是在惹我。

事实证明,本机http插件具有一个uploadFile()函数,我在其他任何地方都没有提到过。 解决方案的作用如下:

  1. 使用fileChooser插件从手机的存储中选择一个文件
  2. 使用filePath插件解析图像的本机文件系统路径。
  3. 使用http.uploadFile()代替http.post()

之所以如此,是因为如上所述,我能够在http插件的标头中正确设置验证令牌,以供控制器接受。

这是代码:

let apiUrl: string = AppConfig.apiUrl + "/UploadImage/";
this.fileChooser.open().then(
  uri => {
    this.filePath.resolveNativePath(uri).then(resolvedPath => {
      loader.present();
      this.http.uploadFile(apiUrl,{ },{ },resolvedPath, "image")
      .then(result => {
        loader.dismiss();
        toastOptions.message = "File uploaded successfully!";
        let toast = this.toastCtrl.create(toastOptions);
        toast.present();
        let json = JSON.parse(result.data);
        this.event.imageUrl = json.imgUrl;
      })
      .catch(err => {
        console.log("error: ", err);
        loader.dismiss();
        toastOptions.message = "Error uploading file";
        let toast = this.toastCtrl.create(toastOptions);
        toast.present();
      });
    });
  }
).catch(
  e => console.log(e)
);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM