如何在 ASP.NET Core MVC 中跨整个应用程序存储用户数据

问题描述 投票:0回答:1

我正在尝试创建一个应用程序,就像卖家管理库存和订单的系统一样。我不想对我的 API 进行数百次调用,因此我想在用户登录后存储用户,并且仅在需要时再次调用 API。我尝试了所有不同的“解决方案”,但没有任何效果。我不想使用 Identity,因为我的 API 不使用它,而且它对于我正在做的事情来说太复杂了。

起初,我将用户存储在单例中

UserService
作为静态属性,但后来我意识到这会导致在应用程序的所有实例中使用相同的 User 属性,因此我尝试添加会话和身份验证,但我只是不这样做了解这一切是如何运作的,但我什至不知道从哪里开始学习。

这是我现在的

UserService

using Market.Data.Models;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Caching.Memory;
using System.Net;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using System.Diagnostics;

namespace Market.Services
{
    public class UserService : IUserService
    {
        private readonly IHttpClientFactory factory;
        private readonly IHttpContextAccessor httpContextAccessor;
        private readonly HttpClient client;
        private User? User;

        public UserService(IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor)
        {
            factory = httpClientFactory;
            client = factory.CreateClient();
            client.BaseAddress = new Uri("https://farmers-market.sommee.com/api/");
            this.httpContextAccessor = httpContextAccessor;
            User = GetUser();
        }

        private async Task SaveUserToContext(User user)
        {
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.UserData, JsonSerializer.Serialize(user)),
            };

            var claimsIdentity = new ClaimsIdentity(
                claims, CookieAuthenticationDefaults.AuthenticationScheme);

            httpContextAccessor.HttpContext.User = new ClaimsPrincipal(claimsIdentity);
        }

        public async Task<User> Login(string email, string password)
        {
            var url = $"https://farmers-market.somee.com/api/users/login?email={email}&password={password}";
            var response = await client.GetAsync(url);
            var result = new User();

            if (response.IsSuccessStatusCode)
            {
                var stringResponse = await response.Content.ReadAsStringAsync();
                Console.WriteLine(stringResponse);

                result = JsonSerializer.Deserialize<User>(stringResponse,
                         new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
                User = result;
                await SaveUserToContext(result!);
            }
            else
            {
                throw new HttpRequestException(response.ReasonPhrase);
            }

            if (result ==  null)
            { 
                throw new Exception("Error with login");
            }

            return result;
        }

        public async Task<HttpStatusCode> Register(User user)
        {
            var url = $"https://farmers-market.somee.com/api/users/add";
            var jsonParsed = JsonSerializer.Serialize<User>(user, new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
            HttpContent content = new StringContent(jsonParsed.ToString(), Encoding.UTF8, "application/json");
            var response = await client.PostAsync(url, content);
            return response.StatusCode;
        }

        public Task RemoveOrderAsync(int orderId)
        {
            if (User == null)
            {
                throw new Exception("User is not authenticated");
            }

            User.SoldOrders.Remove(User.SoldOrders.Single(x => x.Id == orderId));
            return Task.CompletedTask;
        }

        public void AddApprovedOrder(int id)
        {
            User!.SoldOrders.Single(x => x.Id == id).IsApproved = true;
        }

        public void AddDeliveredOrder(int id)
        {
            User!.SoldOrders.Single(x => x.Id == id).IsDelivered = true;
        }

        public User? GetUser()
        {
            var saved = httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.UserData);
            if (saved == null) 
                return null;

            User = JsonSerializer.Deserialize<User>(saved);
            return User;
        }
    }
}

在这种方法中,我将用户保存到

HttpContext
,但在另一个控制器中使用该服务后,保存的
UserData
消失了。

这是我的

program.cs
:

using Market.Services;
using Market.Services.Firebase;
using Market.Services.Inventory;
using Market.Services.Offers;
using Market.Services.Orders;
using Market.Services.Reviews;
using Microsoft.AspNetCore.Authentication.Cookies;
using Market.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Market.Data.Models;

var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddHttpClient();
builder.Services.AddDistributedMemoryCache();

builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(30); // Adjust timeout as needed
    options.Cookie.HttpOnly = true;
    options.Cookie.Name = "SessionCookie_" + Guid.NewGuid().ToString();
    options.Cookie.IsEssential = true;
});

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddCookie(options =>
        {
            // Set a unique cookie name for this instance of the app
            options.Cookie.Name = "AuthCookie_" + Guid.NewGuid().ToString(); // Or use another unique value
        });

builder.Services.AddHttpContextAccessor();

builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<IInventoryService, InventoryService>();
builder.Services.AddScoped<IOfferService, OfferService>();
builder.Services.AddScoped<IFirebaseServive, FirebaseService>();
builder.Services.AddScoped<IOrdersService, OrdersService>();
builder.Services.AddScoped<IReviewsService, ReviewsService>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.UseSession();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

我对所有解决方案或不同方法持开放态度。

c# session asp.net-core-mvc
1个回答
0
投票

在我看来,session和cookie不是存储股票、订单的好方法,并且已经使用了内存中的session或cookie。

首先,内存中的会话将在设置时间和应用程序重新启动后过期,这意味着如果用户正在使用您的应用程序,并且应用程序突然重新启动或发生某些情况,则用户拥有的所有数据都将丢失。会造成很大的使用问题。如果用户想要查询他们使用过的股票、订单,但发生了一些事情导致应用程序无法存储会话中的数据,这会使您的应用程序不可靠。

此外,你还需要考虑你的数据始终是最新的,在我看来,股票和订单变化非常频繁,这意味着会话中的数据是错误的数据,你如何保持所有数据是最新的和正确的一样吗?

总而言之,我仍然不建议你选择session来存储这两个数据。使用API来获取数据是最好的方法,并且在缓存中你可以存储每个用户一些未更改的数据。

在这种方法中,我将用户保存到 HttpContext,但在另一个控制器中使用该服务后,保存的 UserData 消失了。

这与你的 SaveUserToContext 方法有关,SaveUserToContext 方法是将声明添加到 httpcontext 中,但是声明是从 cookie 中读取的,如果你只是在 httpcontext 中添加一些声明,它不会将数据保留到下一个请求中,因为它不会将它们添加到 cookie 中。

如果您想向 cookie 添加新的声明,您需要重新调用

httpContextAccessor.HttpContext.SignInAsync
方法为客户端用户重置 cookie。

代码如下:

private async Task SaveUserToContext(User user)
{
    var claims = new List<Claim>
    {
        new Claim(ClaimTypes.UserData, JsonSerializer.Serialize(user)),
    };

    var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

    var authProperties = new AuthenticationProperties
    {
        IsPersistent = true,
        ExpiresUtc = DateTime.UtcNow.AddMinutes(30), // Set a custom expiry if needed
    };

    await httpContextAccessor.HttpContext.SignInAsync(
        CookieAuthenticationDefaults.AuthenticationScheme,
        new ClaimsPrincipal(claimsIdentity),
        authProperties);
}

结果:

我的测试示例:

    private async Task SaveUserToContext( )
    {
        var claims = new List<Claim>
    {
        new Claim(ClaimTypes.UserData, "JsonSerializer.Serialize(user)"),
    };

        var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

        var authProperties = new AuthenticationProperties
        {
            IsPersistent = true,
            ExpiresUtc = DateTime.UtcNow.AddMinutes(30), // Set a custom expiry if needed
        };

        await _contextAccessor.HttpContext.SignInAsync(
            CookieAuthenticationDefaults.AuthenticationScheme,
            new ClaimsPrincipal(claimsIdentity),
            authProperties);
    }

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.