Blazor + MongoDb Identity:值不能为空。(参数名称“source”)

3

你能帮我吗?我正在试图在MongoDb Identity中使用Blazor,但每次调用signInManager.SignInAsync(user, false)时都会抛出异常:值不能为null。(参数名“source”)

MongoDB服务器版本:4.2.5

ASP.Net Core 3.1

NuGet软件包: AspNetCore.Identity.Mongo: 6.7.0 MongoDB.Driver: 2.10.3

预备步骤: 1.创建一个简单的不带身份验证的Blazor Server项目。 2.然后我只是简单地添加了AspNetCore.Identity.Mongo。

Startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });

        services.Configure<BookstoreDatabaseSettings>(Configuration.GetSection(nameof(BookstoreDatabaseSettings)));
        services.AddSingleton<IBookstoreDatabaseSettings>(sp =>
             sp.GetRequiredService<IOptions<BookstoreDatabaseSettings>>().Value);

        services
            .AddIdentityMongoDbProvider<ApplicationUser, ApplicationRole>(identityOptions =>
            {
                identityOptions.Password.RequiredLength = 1;
                identityOptions.Password.RequireLowercase = false;
                identityOptions.Password.RequireUppercase = false;
                identityOptions.Password.RequireNonAlphanumeric = false;
                identityOptions.Password.RequireDigit = false;
            }, mongoIdentityOptions =>
            {
                mongoIdentityOptions.ConnectionString = Configuration.GetConnectionString("MongoDbDatabase");
            })
            .AddDefaultTokenProviders();

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        });

        services.AddRazorPages();
        services.AddServerSideBlazor();
        services.AddSingleton<WeatherForecastService>();

        services.AddScoped<BookService>();
        services.AddTransient<LoginService>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

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

        app.UseRouting();

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

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapBlazorHub();
            endpoints.MapFallbackToPage("/_Host");
        });
    }
}

ApplicationUser.cs

public class ApplicationUser : MongoUser
{
    public string Name { get; set; }
    public string LastName { get; set; }
    public string Gender { get; set; }
    public DateTime? Birthdate { get; set; }
    public string Country { get; set; }
    public string State { get; set; }
    public string City { get; set; }
}

ApplicationRole.cs

public class ApplicationRole : MongoRole
{
}

RegisterNewUserData.cs

public class RegisterUserData
{
    public string UserName { get; set; }
    public string Gender { get; set; }
    public string Password { get; set; }
    public string ConfirmedPassword { get; set; }
}

LoginService.cs

public class LoginService
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly IConfiguration _configuration;
    private readonly RoleManager<ApplicationRole> _roleManager;

    public LoginService(UserManager<ApplicationUser> userManager,
                                 SignInManager<ApplicationUser> signInManager,
                                 IConfiguration configuration,
                                 RoleManager<ApplicationRole> roleManager)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _configuration = configuration;
        _roleManager = roleManager;
    }

    public async Task<bool> LogIn(LoginUserData loginUser)
    {
        ApplicationUser user = await _userManager.FindByNameAsync(loginUser.UserName);

        if (user != null)
        {
            try
            {  
                var result2 = await _signInManager.PasswordSignInAsync(user.Email, loginUser.Password, loginUser.RememberMe, lockoutOnFailure: true);
                if (result2.Succeeded)
                {
                    return true;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw;
            }
        }
        return false;
    }

    public async Task<bool> RegisterNewUser(RegisterUserData regUser)
    {
        string roleName = "Member";
        var user = new ApplicationUser
        {
            Name = regUser.UserName,
            UserName = regUser.UserName,
            Email = regUser.UserName,
            Gender = regUser.Gender,
            LastName = "",
            Country = "",
            State = "",
            City = ""
        };

        bool isRoleExists = await _roleManager.RoleExistsAsync(roleName);
        if (isRoleExists == false)
        {
            var role = new ApplicationRole();
            role.Name = roleName;
            await _roleManager.CreateAsync(role);
        }

        var result = await _userManager.CreateAsync(user, regUser.Password);
        if (result.Succeeded)
        {
            await _userManager.AddToRoleAsync(user, roleName);

            var claims = new List<Claim>
            {
                new Claim("user", user.UserName),
                new Claim("role", roleName)
            };
            result = await _userManager.AddClaimsAsync(user, claims);

            if (result.Succeeded)
            {
                try
                {
                    await _signInManager.SignInAsync(user, false);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    throw;
                }                    
                return true;
            }
        }
        return false;
    }
}

注册过程中,用户、角色和声明已经成功创建并添加。以下是来自MongoDb的数据:

{
  "_id": {
    "$oid": "5e9df2819fecf83484901211"
  },
  "UserName": "M1@g.com",
  "NormalizedUserName": "M1@G.COM",
  "Email": "M1@g.com",
  "NormalizedEmail": "M1@G.COM",
  "EmailConfirmed": false,
  "PasswordHash": "AQAAAAEAACcQAAAAEGdbk1EC4niPacknWuMDpbc+YRZP5CmvH0IaUIslo5/vcHplpJO/iWBU/6opCYsErQ==",
  "SecurityStamp": "BOYJUYQFPMLMHJ6NFBHG64K4SC7WEF5W",
  "ConcurrencyStamp": "e9133395-1eed-4757-91a5-a5fc1d699f5d",
  "PhoneNumber": null,
  "PhoneNumberConfirmed": false,
  "TwoFactorEnabled": false,
  "LockoutEnd": null,
  "LockoutEnabled": true,
  "AccessFailedCount": 0,
  "AuthenticatorKey": null,
  "Roles": [
    "5e9df27c9fecf83484901210"
  ],
  "Claims": [
    {
      "_id": 0,
      "UserId": null,
      "ClaimType": "user",
      "ClaimValue": "M1@g.com"
    },
    {
      "_id": 0,
      "UserId": null,
      "ClaimType": "role",
      "ClaimValue": "Member"
    }
  ],
  "Logins": [],
  "Tokens": [],
  "RecoveryCodes": [],
  "Name": "M1@g.com",
  "LastName": "",
  "Gender": "Male",
  "Birthdate": null,
  "Country": "",
  "State": "",
  "City": ""
}

但是当进程调用:

_signInManager.SignInAsync(user, false);

或者:

await _signInManager.PasswordSignInAsync(user.Email, loginUser.Password, loginUser.RememberMe, lockoutOnFailure: true);

我收到了异常:值不能为null(参数名称“source”)。那么,我错过了什么?

注意,我使用的是与ASP.Net Core 2.1 + Razor Pages + MongoDb Identity相同的解决方案,并且这个解决方案运行良好。


我目前正在解决同样的问题,我相当肯定这是因为你的ApplicationUser类中的某个属性在数据库中不存在。例如:class User{ public string Name {get;set;} public string[] PetNames {get; set;} }如果bson文档如下所示,则会抛出异常:{ "Name":"jAndersen" } - jAndersen
尝试手动设置BirthDate的值,看看是否有效。最后查看这个问题:https://dev59.com/x2LVa4cB1Zd3GeqPxIQS - jAndersen
1个回答

0

我想我找到了问题所在,至少对于登录错误而言是这样。我不知道我们是否会在其他用户管理语句中发现类似的错误。 但基本上,错误信息如下:

System.ArgumentNullException: Value cannot be null. (Parameter 'source') at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) at System.Linq.Enumerable.Select[TSource,TResult](IEnumerable1 source, Func2 selector) at AspNetCore.Identity.Mongo.Stores.RoleStore`1.GetClaimsAsync(TRole role, CancellationToken cancellationToken)

起初,我认为这与我创建的用户没有任何声明有关。因此,就像您在此处编写的代码一样,我在注册方法和管理员用户种子中也实现了它。在我的用户拥有声明之后,我仍然收到相同的错误。所以过了一段时间,我真的停下来试图理解错误消息。

如果我们从下往上阅读它,我们首先看到调用RoleStore的GetClaimAsync方法。然后调用System.Linq的Select,然后我们有一个参数“source”,它是抛出nu,,参数异常的参数。

查看此插件的源代码,我们可以看到GetClaimsAsync具有以下语句:

return dbRole.Claims.Select(e => new Claim(e.ClaimType, e.ClaimValue)).ToList();

dbRole.Claims 指的是 Roles 集合内的 Claims 对象。我的集合中没有设置 Claims 对象,它们是 null。

因此,在我的 Roles 种子数据填充方法中,我为每个创建的角色填充了 Claims 对象。之后,我就能够成功登录而不会出现 ArgumentNullException 异常了。

await roleManager.CreateAsync(new MongoRole
                        {
                            Name = role,
                            Claims = new List<IdentityRoleClaim<string>> {
                                new IdentityRoleClaim<string> {
                                    ClaimType = "role",
                                    ClaimValue = role
                                }
                            }
                        });

现在,我想知道我们是否会遇到这些 Claims 对象的其他问题。


你是否已经完全解决了你的问题?这只是在登录功能中的claims对象吗? - Przemo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接