.net 将CookiesCollection保存到文件并在点NET MAUI C#中读取

mwecs4sa  于 2023-08-08  发布在  .NET
关注(0)|答案(1)|浏览(86)

我试图建立一个应用程序在MAUI将只工作在android。这个应用程序很简单,不需要任何特殊的安全性或其他东西。我主要在使用HttpClient时遇到了一个问题,我创建了一个继承自HttpClientHandler的类和一个继承自CookieContainer的类。

HttpClient:

public class ApiHttpClient : HttpClient
{

    #region FIELDS

    private readonly IUriService _uriService;

    #endregion FIELDS

    #region CTORS

    public ApiHttpClient(IUriService uriService, HttpClientHandler httpClientHandler) : base(httpClientHandler)
    {
        _uriService = uriService;
    }

    #endregion CTORS

    /// <summary>
    /// 
    /// </summary>
    /// <param name="loginUserInfo"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <exception cref="UnauthorizedApiException"></exception>
    /// <exception cref="BadRequestApiException"></exception>
    /// <exception cref="ArgumentException"></exception>
    public async Task SignInAsync(ILoginUserInfo loginUserInfo, CancellationToken? cancellationToken = null)
    {
        var uri = _uriService.AuthenticationSignInPath();
        var httpResponseMessage = await this.PostAsJsonAsync(uri, loginUserInfo,
            cancellationToken ?? new CancellationToken(false));

        if (httpResponseMessage.StatusCode == HttpStatusCode.OK)
        {
            return;
        }
        if(httpResponseMessage.StatusCode == HttpStatusCode.Unauthorized)
        {
            var problemDetails = await httpResponseMessage.Content.ReadFromJsonAsync<ProblemDetails>();
            throw new UnauthorizedApiException(problemDetails?.Detail);
        }
        if (httpResponseMessage.StatusCode == HttpStatusCode.BadRequest)
        {
            var problemDetails = await httpResponseMessage.Content.ReadFromJsonAsync<ProblemDetails>();
            throw new BadRequestApiException(problemDetails?.Detail);
        }

        throw new ArgumentException($"Unexpected error! Status code: {httpResponseMessage.StatusCode}.", nameof(httpResponseMessage.StatusCode));
    }
}

字符串

HttpClientHandler:

public class ApiHttpClientHandler : HttpClientHandler
{

    #region FIELDS

    private readonly CookiesService.CookiesService _cookiesService;

    #endregion FIELDS

    #region CTORS

    public ApiHttpClientHandler(CookiesService.CookiesService cookiesService)
    {
        _cookiesService = cookiesService;

        UseCookies = true;
        CookieContainer = _cookiesService;
    }

    protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var response = base.Send(request, cancellationToken);
        _cookiesService.SaveCookies();
        return response;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        //TODO cookies are not sent with first request
        var response = await base.SendAsync(request, cancellationToken);
        _cookiesService.SaveCookies();
        return response;
    }

    #endregion CTORS

}

CookieContainer:

public class CookiesService : CookieContainer
{

    #region FIELDS
    
    private const string COOKIES_FILE_NAME = "biscuits.js";

    private string? _cookiesFilePath;

    #endregion FIELDS

    #region CTORS

    public CookiesService()
    {
        _cookiesFilePath = Path.Combine(FileSystem.CacheDirectory, COOKIES_FILE_NAME);
        LoadCookies();
    }

    #endregion CTORS

    #region METHODS

    public void LoadCookies()
    {
        if (!File.Exists(_cookiesFilePath))
        {
            return;
        }

        var cookieFileContent = File.ReadAllText(_cookiesFilePath);
        if (string.IsNullOrWhiteSpace(cookieFileContent))
            return;
        var deserializedCookies = JsonSerializer.Deserialize<CookieCollection>(cookieFileContent);

        if (deserializedCookies != null)
            Add(deserializedCookies);

        var cookies = GetAllCookies();
    }

    public void SaveCookies()
    {
        var cookies = GetAllCookies();

        if (cookies.Count <= 0) return;

        var serializedCookies = JsonSerializer.Serialize(cookies, new JsonSerializerOptions() {WriteIndented = true});
        File.WriteAllText(_cookiesFilePath, serializedCookies);
    }

    #endregion METHODS

}


所以CookiesService用于保存Load来自缓存的JSON文件的cookie,当我加载cookie时,它们被正确加载,但当我从我的HTTP客户端发送请求进行身份验证时,即使我将身份验证cookie加载到CookiesService中,它不会发送到服务器,因为服务器是我的,我已经拦截了HEADERS,但身份验证令牌没有随请求一起发送,因此它进行身份验证并响应如果应用程序在请求完成后保存到文件中,最奇怪的是,如果我运行相同的请求两次而不关闭应用程序,它会发送身份验证令牌,服务器会以Unauthorized状态代码响应,这意味着用户已经登录,所以令牌被正确发送。
总之,我想问为什么如果我从文件中加载cookie,并且我在发送请求之前检查了cookie是否正确加载,认证cookie不会被发送。

**小更新:**我尝试在发送请求之前注入相同的名称,值,路径和域的相同cookie,这种方式工作,但我必须手工创建。如果我将身份验证令牌cookie保存到文件中,并将其加载回去,然后手动插入一个值和名称无意义但域和路径正确的cookie,则cookie的IEEnumerator的调试器结果视图显示有两个cookie,但服务器只接收其中一个,即手工创建的,而不是从文件中加载的。
**另一个小更新:**我测试了只有从文件加载的cookie不发送的事实,我尝试注入一个假cookie,该假cookie是在注入时发送的,从服务器接收,然后保存到文件中,但在重新启动应用程序后,读取cookie文件并加载它们(假cookie与文件中的身份验证令牌一起加载),没有注入新的假cookie,从文件加载的cookie都不会发送到服务器,令牌和假cookie都没有。现在我只需要找出为什么以及如何避免这种行为。现在我试过了,但它不工作:
烹饪服务:

public void LoadCookies()
{
    if (!File.Exists(_cookiesFilePath))
    {
        return;
    }
    var cookieFileContent = File.ReadAllText(_cookiesFilePath);
    if (string.IsNullOrWhiteSpace(cookieFileContent))
        return;
    var deserializedCookies = JsonSerializer.Deserialize<CookieCollection>(cookieFileContent);

    if (deserializedCookies != null)
        foreach(var cookie in deserializedCookies.ToArray()) 
            Add(new Cookie()
            {
                Name = cookie.Name,
                Value = cookie.Value,
                Expires = cookie.Expires,
                Path = cookie.Path,
                Comment = cookie.Comment,
                HttpOnly = cookie.HttpOnly,
                Expired = cookie.Expired,
                CommentUri = cookie.CommentUri,
                Discard = cookie.Discard,
                Domain = cookie.Domain,
                Port = cookie.Port,
                Secure = cookie.Secure,
                Version = cookie.Version
            });
}


我甚至尝试使用反射来查看服务器发送的cookie和存储的cookie之间的差异,没有任何有价值的差异。

另一个更新:我找到了一个变通方法,让HttpClientHandler不发送cookie(我会做进一步调试以了解是哪一个),现在工作代码在CookiesService内部:
烹饪服务:

public void LoadCookies()
{
    if (!File.Exists(_cookiesFilePath))
        return;

    var cookieFileContent = File.ReadAllText(_cookiesFilePath);
    if (string.IsNullOrWhiteSpace(cookieFileContent))
        return;
    var deserializedCookies = JsonSerializer.Deserialize<CookieCollection>(cookieFileContent);

     if (deserializedCookies != null)
        foreach(var cookie in deserializedCookies.ToArray())
        {
            var cookieCopy = new Cookie(cookie.Name, cookie.Value, cookie.Path, cookie.Domain);
            cookieCopy.HttpOnly = true;
            Add(cookieCopy);
        }
}

goqiplq2

goqiplq21#

我找到了解决方案,如果有人有一个优化的方法,请张贴答案。
所以在调试之后,我发现没有发送cookie的属性是Port属性,我不明白为什么,但它是唯一这样做的属性,所以我的新工作代码是:

public void LoadCookies()
{
    if (!File.Exists(_cookiesFilePath))
    {
        return;
    }

    var cookieFileContent = File.ReadAllText(_cookiesFilePath);
    if (string.IsNullOrWhiteSpace(cookieFileContent))
        return;
    var deserializedCookies = JsonSerializer.Deserialize<CookieCollection>(cookieFileContent);

    if (deserializedCookies != null)
        foreach(var cookie in deserializedCookies.ToArray())
        {
            var cookieCopy = new Cookie(cookie.Name, cookie.Value, cookie.Path, cookie.Domain)
            {
                HttpOnly = cookie.HttpOnly,
                Comment = cookie.Comment,
                CommentUri = cookie.CommentUri,
                Discard = cookie.Discard,
                Expired = cookie.Expired,
                Expires = cookie.Expires,
                Secure = cookie.Secure,
                Version = cookie.Version,
                /* If set, this property is not making the
                 * HttpClientHandler to send the cookie loaded from the cookies file */
                /* Port = cookie.Port,*/
            };
            Add(cookieCopy);
        }
}

字符串

相关问题