登录到 ASP.NET Core 应用程序在 Android 真实设备上无法使用 Xamarin.Forms 应用程序中的 .NET HttpClient



有一个Xamarin.Forms应用程序,该应用程序通过 ASP.NET Core中实现的API请求数据。在检索数据之前,应用必须登录到使用标识服务的 ASP.Net Core 应用程序。该代码在iOS模拟器,Android 9模拟器和真正的iPhone上运行良好,但在真正的Android 9设备上则不行!网络连接运行良好,因为我可以通过浏览器访问 Web 应用程序。

我在共享项目中使用 System.Net.Http.HttpClient。身份服务需要发送防伪密钥(顺便问一下:有没有更好的方法来处理仅针对 Web API 的授权?处理防伪钥匙似乎不方便(。以下是处理登录过程的代码:

private async Task LoginAsync(string username, string password)
{
//Get login page with AntiForgeryToken
var pageWithToken = await _httpClient.GetStringAsync(BASE_URL);
//Extract AntiForgeryToken
string verificationToken = GetAntiForgeryToken(pageWithToken);
//Post parameter
var keyValues = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("Input.Email", username),
new KeyValuePair<string, string>("Input.Password", password),
new KeyValuePair<string, string>("__RequestVerificationToken", verificationToken),
new KeyValuePair<string, string>("Input.RememberMe", "false")
};
var request = new HttpRequestMessage(HttpMethod.Post, "Identity/Account/Login")
{
Content = new FormUrlEncodedContent(keyValues)
};
//login with Post parameter
var response = await _httpClient.SendAsync(request);
//read response - can see here that login failed
var content = await response.Content.ReadAsStringAsync();
}

这是我在发送带有登录数据的 Post 请求后在服务器端看到的内容:

Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 POST http://10.219.200.147:33224/Identity/Account/Login application/x-www-form-urlencoded 253
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executing endpoint 'Page: /Account/Login'
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Route matched with {page = "/Account/Login", area = "Identity", action = "", controller = ""}. Executing page /Account/Login
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executing handler method C4S.WebApp.Areas.Identity.Pages.Account.LoginModel.OnPostAsync with arguments () - ModelState is Valid
Microsoft.EntityFrameworkCore.Infrastructure:Information: Entity Framework Core 2.2.6-servicing-10079 initialized 'ApplicationDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: None
Der Thread 0x28d4 hat mit Code 0 (0x0) geendet.
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (9ms) [Parameters=[@__normalizedUserName_0='?' (Size = 256)], CommandType='Text', CommandTimeout='30']
SELECT TOP(1) [u].[Id], [u].[AccessFailedCount], [u].[ConcurrencyStamp], [u].[Email], [u].[EmailConfirmed], [u].[LockoutEnabled], [u].[LockoutEnd], [u].[NormalizedEmail], [u].[NormalizedUserName], [u].[PasswordHash], [u].[PhoneNumber], [u].[PhoneNumberConfirmed], [u].[SecurityStamp], [u].[TwoFactorEnabled], [u].[UserName]
FROM [AspNetUsers] AS [u]
WHERE [u].[NormalizedUserName] = @__normalizedUserName_0
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (2ms) [Parameters=[@__user_Id_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30']
SELECT [uc].[Id], [uc].[ClaimType], [uc].[ClaimValue], [uc].[UserId]
FROM [AspNetUserClaims] AS [uc]
WHERE [uc].[UserId] = @__user_Id_0
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:Information: AuthenticationScheme: Identity.Application signed in.
C4S.WebApp.Areas.Identity.Pages.Account.LoginModel:Information: User logged in.
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executed handler method OnPostAsync, returned result Microsoft.AspNetCore.Mvc.LocalRedirectResult.
Microsoft.AspNetCore.Mvc.Infrastructure.LocalRedirectResultExecutor:Information: Executing LocalRedirectResult, redirecting to /.
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executed page /Account/Login in 112.8493ms
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executed endpoint 'Page: /Account/Login'
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 131.0183ms 302 
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://10.219.200.147:33224/  
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executing endpoint 'Page: /Kontakte/Index'
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Route matched with {page = "/Kontakte/Index", action = "", controller = "", area = ""}. Executing page /Kontakte/Index
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed.
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.ChallengeResult:Information: Executing ChallengeResult with authentication schemes ().
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:Information: AuthenticationScheme: Identity.Application was challenged.
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executed page /Kontakte/Index in 6.2539ms
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executed endpoint 'Page: /Kontakte/Index'
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 14.9729ms 302 
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://10.219.200.147:33224/Identity/Account/Login?ReturnUrl=%2F  
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executing endpoint 'Page: /Account/Login'
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Route matched with {page = "/Account/Login", area = "Identity", action = "", controller = ""}. Executing page /Account/Login
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executing handler method C4S.WebApp.Areas.Identity.Pages.Account.LoginModel.OnGetAsync with arguments (/) - ModelState is Valid
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:Information: AuthenticationScheme: Identity.External signed out.
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executed handler method OnGetAsync, returned result .
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executing an implicit handler method - ModelState is Valid
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executed an implicit handler method, returned result Microsoft.AspNetCore.Mvc.RazorPages.PageResult.
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executed page /Account/Login in 11.6849ms
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executed endpoint 'Page: /Account/Login'
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 20.9535ms 200 text/html; charset=utf-8

我想知道为什么日志首先显示"用户登录",然后显示"授权失败"。会不会有饼干问题?只是说,我正在一个没有加密的测试环境中工作 ->只有http。

感谢您的帮助!

终于找到了解决方案!我认为身份 cookie 有问题,因为登录成功,但进一步的请求似乎忘记了这一点。Identity 在客户端存储一个 cookie,以便在成功登录后进行身份验证,并在每次请求时将其发送到服务器。在启动时访问 HttpClient 的 cookie 需要一些额外的代码:

HttpClientHandler _httpClientHandler = new HttpClientHandler();
HttpClient _httpClient = new HttpClient(_httpClientHandler) { BaseAddress = new Uri(BASE_URL), Timeout = new TimeSpan(0, 0, 30) };

将 HttpClienthandler 添加到 HttpClient 中就成功了!我不确定为什么Android需要它,而不是iOS需要它。也许有人有猜测?