在 Xamarin 上的 Asp Net 5.0 中使用 AntiForgeryToken 访问 Web API

Posted

技术标签:

【中文标题】在 Xamarin 上的 Asp Net 5.0 中使用 AntiForgeryToken 访问 Web API【英文标题】:Access WebAPI with AntiForgeryToken in Aspnet 5.0 on Xamarin 【发布时间】:2017-02-14 19:59:40 【问题描述】:

我正在尝试访问使用 ValidateAntiForgeryToken 的 WebAPI。我的 WebAPI 方法是这样的(一个简单的),它位于用户控制器内部(仅用于测试):

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Test(String field)

    String result = String.Empty;
    if (ModelState.IsValid)
    
        htmlSanitizer sanitizer = new HtmlSanitizer();

        try
        
            result = sanitizer.Sanitize(field);
        
        catch (Exception ex)
        
            result = ex.Message;
            throw;
         
    
    return Json(result);

使用 Ajax,我可以轻松访问它:

$.ajax(
    url: '/User/Test',
     type: "POST",
    contentType: "application/x-www-form-urlencoded",

    data: 

        field: self.textField(),
         __RequestVerificationToken: $("input[name='__RequestVerificationToken']").val(),
    ,
    success: function(e) 
       self.textField(e)
        self.divField(e);
    ,
    error: function(e) 
        console.log(e.error());
    ,
);

但是,直到现在,我无法在 xamarin 上使用 httpclient 访问这个 webapi。这是我的代码:

    private async void DoTestWebApi()

    try
    
        HttpClient clientPage = new HttpClient()
        
            BaseAddress = new Uri("https://localhost:44356/user")
        ;

        var pageWithToken = await clientPage.GetAsync(clientPage.BaseAddress);

        String verificationToken = GetVerificationToken(await pageWithToken.Content.ReadAsStringAsync());

        HttpClient client = new HttpClient()
        
            BaseAddress = new Uri("https://localhost:44356/user/test/")
        ;

        HttpRequestMessage message = new HttpRequestMessage()
        
            RequestUri = new Uri("https://localhost:44356/user/test/"),
            Method = HttpMethod.Post
        ;

        message.Headers.Add("__RequestVerificationToken", verificationToken);

        String field = "teste";

        //StringContent content = new StringContent("field=test", Encoding.UTF8, "application/x-www-form-urlencoded");
        StringContent content = new StringContent("__RequestVerificationToken=" + verificationToken + ",field=test", Encoding.UTF8, "application/x-www-form-urlencoded");

        // this doesn't work
        //client.DefaultRequestHeaders.Add("__RequestVerificationToken", verificationToken);
        var response2 = await client.SendAsync(message);

        if (response2.IsSuccessStatusCode)
        
            var t = response2.Content.ReadAsStringAsync();

            if (true)
            
                // just to check if t has value
            
        

    
    catch (Exception ex)
    
        Console.WriteLine(ex.Message);
        throw;
    


老实说,我不知道我还能做些什么来在消息中传递我的防伪令牌。它在 ajax 中完美运行,我在数据内容中传递它,但在 xamarin 中它不起作用。 所有代码都在同一个本地主机内执行。如果我删除 [ValidateAntiForgeryToken],它可以工作。

我错过了什么?

编辑:

好的,现在我使用 cookie 发送,但不再使用该方法。 这是我的更新:

HttpClient clientPage = new HttpClient()

    BaseAddress = new Uri("https://localhost:44356/user")
;

var pageWithToken = await clientPage.GetAsync(clientPage.BaseAddress);

String verificationToken = GetVerificationToken(await pageWithToken.Content.ReadAsStringAsync());

List<KeyValuePair<String, String>> cookiesInfo = new List<KeyValuePair<String, String>>();

foreach (var item in pageWithToken.Headers)

    cookiesInfo.Add(new KeyValuePair<String, String>(item.Key, item.Value.ToString()));


cookiesInfo.Add(new KeyValuePair<string, string>("field", "value"));
cookiesInfo.Add(new KeyValuePair<string, string>("__RequestVerificationToken", verificationToken));

CookieContainer cookieContainer = new CookieContainer();

using (var handler = new HttpClientHandler()  CookieContainer = cookieContainer )

    using (var client = new HttpClient(handler)  BaseAddress = new Uri("https://localhost:44356/user") )
    
        var content = new FormUrlEncodedContent(cookiesInfo);

        cookieContainer.Add(client.BaseAddress, new Cookie("__RequestVerificationToken", verificationToken));

        foreach (var item in cookiesInfo)
        
            cookieContainer.Add(client.BaseAddress, new Cookie(item.Key, item.Value));
        

        var result = client.PostAsync(new Uri("https://localhost:44356/user/test"), content).Result;

        result.EnsureSuccessStatusCode();
    

;

这快把我逼疯了……好吧,测试是在 localhost 中,但很快这个应用程序就会在 Azure 中,这是一个先决条件……


编辑:GetVerificationToken 方法:

private string GetVerificationToken(String verificationToken)
    
        if (verificationToken != null && verificationToken.Length > 0)
        
            verificationToken = verificationToken.Substring(verificationToken.IndexOf("__RequestVerificationToken"));
            verificationToken = verificationToken.Substring(verificationToken.IndexOf("value=\"") + 7);
            verificationToken = verificationToken.Substring(0, verificationToken.IndexOf("\""));
        

        return verificationToken;
    

【问题讨论】:

您能否使用Fiddler 之类的工具捕获发送到您的API 的请求,并将ajax 与HttpClient 进行比较。这可能会为您提供一些关于差异的线索。 localhost?在您的手机/模拟器中使用的 localhost 将是手机/模拟器,而不是您的 PC。使用运行您的 aspnet/iis 应用程序的主机的主机名/IP 地址,该应用程序可从手机/模拟器解析,尝试使用设备的浏览器打开该 url/页面作为测试... 是的,本地主机。因为所有应用程序都在同一台机器上执行。它有效,这不是问题(如果我删除了 ValidateAntiForgeryToken,运行良好) 【参考方案1】:

ValidateAntiForgeryToken 还需要一个带有__RequestVerificationToken 和提供的值的cookie。这是为了确保向控制器发帖的人是查看表单的人。

【讨论】:

我已经更新了我的问题,但似乎 cookie 不仅仅是问题所在。我现在用 cookie 发送。我正在尝试使用名为 Advanced Rest Client (Chrome) 的工具,即使我在此工具中设置了 cookie,它也不起作用。【参考方案2】:

感谢@Zroq 提示,我终于成功了。饼干确实不见了。这是我在 Asp.NET MVC 5.0 中使用 AntiForgeryToken 向 WebApi 发送数据的方法的最终版本:

private async void DoTestWebApi()
    
        try
        
            CookieContainer cookieContainer = new CookieContainer();

            HttpClientHandler handlerhttps = new HttpClientHandler
            
                UseCookies = true,
                UseDefaultCredentials = true,
                CookieContainer = cookieContainer
            ;

            HttpClient clientPage = new HttpClient(handlerhttps)
            
                BaseAddress = new Uri("https://localhost:44356/user")
            ;

            var pageWithToken = await clientPage.GetAsync(clientPage.BaseAddress);

            String verificationToken = GetVerificationToken(await pageWithToken.Content.ReadAsStringAsync());

            var cookies = cookieContainer.GetCookies(new Uri("https://localhost:44356/user"));

            using (var handler = new HttpClientHandler()  CookieContainer = cookieContainer, UseDefaultCredentials = true, UseCookies = true )
            
                using (var client = new HttpClient(handler)  BaseAddress = new Uri("https://localhost:44356/user/test") )
                
                    var contentToSend = new FormUrlEncodedContent(new[]
                    
                        new KeyValuePair<string, string>("field", "value"),
                        new KeyValuePair<string, string>("__RequestVerificationToken", verificationToken),
                    );

                    var response = client.PostAsync(client.BaseAddress, contentToSend).Result;
                
            ;
        
        catch (Exception ex)
        
            Console.WriteLine(ex.Message);
        
    

再次感谢@Zroq。

【讨论】:

Hi.GetVerificationToken() 方法在哪里? 有没有办法发送对象而不是字符串?我不想在服务器端使用 Json.Deserialized 。因为我想使用 ModelState。 我现在不在我的 PC 中,但是 AFAIK 您可以将其作为 StringContent 发送,您将 JSON 对象发送到您的服务器:***.com/questions/11145053/… 和 ***.com/questions/18971510/… 此方法只需要一个 JSON 对象或字符串,据我所知,您不能将自定义对象发送到其中。 是的,我尝试了这些方法,但我的对象在服务器端总是空的。 如何将验证令牌放入类似这样的东西: var stringContent = new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json"); var response = await httpClient.PostAsync(url, stringContent);【参考方案3】:

对于任何想要 GetVerificationToken() 正文的人:

private string GetVerification(string responseBody)

     var data = QueryHelpers.ParseQuery(queryString: responseBody);
     string firstValue = data[key: "<input name"];
     var cutedValue = firstValue.Remove(startIndex: 0, count: 50);
     var result = cutedValue.Split('"')[0];
     return result;

【讨论】:

我已经在问题上添加了我的 GetVerificationToken 方法。 对不起,我没看到。

以上是关于在 Xamarin 上的 Asp Net 5.0 中使用 AntiForgeryToken 访问 Web API的主要内容,如果未能解决你的问题,请参考以下文章

Xamarin Android 使用 Asp.Net core web api 连接到 Azure Notification Hub [关闭]

从 Xamarin 对 Asp.NET CORE Web 应用程序进行身份验证

Xamarin Mobile 使用的 ASP.Net Core Web API

IIS 5.0 和 6.0 的 ASP.NET 应用程序生命周期概述

如何按照存储库模式在 Asp.Net Core 5.0 项目上实现 .Net Core Identity?

AddCors 在 asp.net 5.0 中不起作用。如何启用它?