当前位置: 首页 > 知识库问答 >
问题:

如何在ASP. NET身份中消费和ASP. NET会员数据库?

祝允晨
2023-03-14

我有几个传统的ASP. NET Web应用程序,它们共享一个ASP. NET成员资格数据库。我想迁移到使用. NET Core和IdtyServer4的微服务架构,并在新的微服务生态系统中拥有身份服务器来使用现有的ASP. NET成员资格用户存储,但是。NET Core似乎根本不支持ASP. NET成员资格。

我目前有一个概念验证,涉及一个web API、identity server和一个MVC web app作为我的客户端。identity server实现了IdentityUser的一个子类,并实现了IUserStore/IUserPasswordStore/IUserEmailStore以使其适应ASP。现有数据库中的NET成员资格表。我可以注册新用户并通过我的POC MVC客户端应用程序登录,但这些用户无法登录我的旧应用程序。相反,在旧版应用程序中注册的用户无法登录到我的POC MVC客户端。我认为这是因为我的IPasswordHasher实现对密码的哈希处理与ASP不同。我的旧版应用程序中的NET成员身份。

下面是我的代码。如果能了解我可能做错了什么,我将不胜感激。安全和密码学不是我的强项。

启动。反恐精英

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

        if (env.IsDevelopment())
        {
            // For more details on using the user secret store see https://go.microsoft.com/fwlink/?LinkID=532709
            builder.AddUserSecrets<Startup>();
        }

        builder.AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        /* Add CORS policy */
        services.AddCors(options =>
        {
            // this defines a CORS policy called "default"
            options.AddPolicy("default", policy =>
            {
                policy.WithOrigins("http://localhost:5003")
                    .AllowAnyHeader()
                    .AllowAnyMethod();
            });
        });
        services.AddMvcCore()
            .AddAuthorization()
            .AddJsonFormatters();

        /* Add MVC componenets. */
        services.AddMvc();

        /* Configure IdentityServer. */
        services.Configure<IdentityOptions>(options =>
        {
            // Password settings
            options.Password.RequireDigit = true;
            options.Password.RequiredLength = 8;
            options.Password.RequireNonAlphanumeric = false;
            options.Password.RequireUppercase = true;
            options.Password.RequireLowercase = false;

            // Lockout settings
            options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
            options.Lockout.MaxFailedAccessAttempts = 10;

            // Cookie settings
            options.Cookies.ApplicationCookie.ExpireTimeSpan = TimeSpan.FromDays(150);
            options.Cookies.ApplicationCookie.LoginPath = "/Account/Login";
            options.Cookies.ApplicationCookie.LogoutPath = "/Account/Logout";

            // User settings
            options.User.RequireUniqueEmail = true;
        });

        /* Add the DbContext */
        services.AddDbContext<StoreContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("MyConnectionString")));

        /* Add ASP.NET Identity to use for registration and authentication. */
        services.AddIdentity<AspNetMembershipUser, IdentityRole>()
            .AddEntityFrameworkStores<StoreContext>()
            .AddUserStore<AspNetMembershipUserStore>()
            .AddDefaultTokenProviders();

        services.AddTransient<IPasswordHasher<AspNetMembershipUser>, AspNetMembershipPasswordHasher>();

        /* Add IdentityServer and its components. */
        services.AddIdentityServer()
            .AddInMemoryCaching()
            .AddTemporarySigningCredential()
            .AddInMemoryApiResources(Config.GetApiResources())
            .AddInMemoryIdentityResources(Config.GetIdentityResources())
            .AddInMemoryClients(Config.GetClients());
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        /* Configure logging. */
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));

        if (env.IsDevelopment())
        {
            loggerFactory.AddDebug();
            app.UseDeveloperExceptionPage();
            app.UseDatabaseErrorPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        /* Configure wwwroot */
        app.UseStaticFiles();

        /* Configure CORS */
        app.UseCors("default");

        /* Configure AspNet Identity */
        app.UseIdentity();

        /* Configure IdentityServer */
        app.UseIdentityServer();

        /* Configure MVC */
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

AspNetMembershipUser。反恐精英

public class AspNetMembershipUser : IdentityUser
{
    public string PasswordSalt { get; set; }
    public int PasswordFormat { get; set; }
}

AspNetMembershipUserStore。反恐精英

public class AspNetMembershipUserStore : IUserStore<AspNetMembershipUser>, IUserPasswordStore<AspNetMembershipUser>, IUserEmailStore<AspNetMembershipUser>
{
    private readonly StoreContext _dbcontext;

    public AspNetMembershipUserStore(StoreContext dbContext)
    {
        _dbcontext = dbContext;
    }

    public Task<IdentityResult> CreateAsync(AspNetMembershipUser user, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() =>
        {
            try
            {
                User dbUser = new User();
                this.Convert(user, dbUser);
                _dbcontext.Users.Add(dbUser);
                _dbcontext.SaveChanges();
                return IdentityResult.Success;
            }
            catch (Exception ex)
            {
                return IdentityResult.Failed(new IdentityError
                {
                    Code = ex.GetType().Name,
                    Description = ex.Message
                });
            }
        });
    }

    public Task<IdentityResult> DeleteAsync(AspNetMembershipUser user, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() =>
        {
            try
            {
                User dbUser = _dbcontext.Users
                    .Include(u => u.AspNetUsers).ThenInclude(u => u.AspNetMembership)
                    .Include(u => u.AspNetUsers).ThenInclude(u => u.AspNetApplication)
                    .Include(u => u.UserGroups)
                    .SingleOrDefault(u => u.ProviderUserName == user.NormalizedUserName);

                if (dbUser != null)
                {
                    _dbcontext.AspNetUsers.Remove(dbUser.AspNetUser);
                    _dbcontext.Users.Remove(dbUser);
                    _dbcontext.SaveChanges();
                }

                return IdentityResult.Success;
            }
            catch (Exception ex)
            {
                return IdentityResult.Failed(new IdentityError
                {
                    Code = ex.GetType().Name,
                    Description = ex.Message
                });
            }
        });
    }

    public void Dispose()
    {
        _dbcontext.Dispose();
    }

    public Task<AspNetMembershipUser> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() =>
        {
            User dbUser = _dbcontext.Users
                .Include(u => u.AspNetUsers).ThenInclude(u => u.AspNetMembership)
                .Include(u => u.AspNetUsers).ThenInclude(u => u.AspNetApplication)
                .Include(u => u.UserGroups)
                .SingleOrDefault(u => u.ProviderEmailAddress == normalizedEmail);

            if (dbUser == null)
            {
                return null;
            }

            AspNetMembershipUser user = new AspNetMembershipUser();
            this.Convert(dbUser, user);
            return user;
        });
    }

    public Task<AspNetMembershipUser> FindByIdAsync(string userId, CancellationToken cancellationToken)
    {
        long lUserId = long.Parse(userId);
        return Task.Factory.StartNew(() =>
        {
            User dbUser = _dbcontext.Users
                .Include(u => u.AspNetUsers).ThenInclude(u => u.AspNetMembership)
                .Include(u => u.AspNetUsers).ThenInclude(u=> u.AspNetApplication)
                .Include(u => u.UserGroups)
                .SingleOrDefault(u => u.UserId == lUserId);

            if (dbUser == null)
            {
                return null;
            }

            AspNetMembershipUser user = new AspNetMembershipUser();
            this.Convert(dbUser, user);
            return user;
        });
    }

    public Task<AspNetMembershipUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() =>
        {
            User dbUser = _dbcontext.Users
                .Include(u => u.AspNetUsers).ThenInclude(u => u.AspNetMembership)
                .Include(u => u.AspNetUsers).ThenInclude(u => u.AspNetApplication)
                .Include(u => u.UserGroups)
                .SingleOrDefault(u => u.ProviderUserName == normalizedUserName);

            if (dbUser == null)
            {
                return null;
            }

            AspNetMembershipUser user = new AspNetMembershipUser();
            this.Convert(dbUser, user);
            return user;
        });
    }

    public Task<string> GetEmailAsync(AspNetMembershipUser user, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() => user.Email);
    }

    public Task<bool> GetEmailConfirmedAsync(AspNetMembershipUser user, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() => user.EmailConfirmed);
    }

    public Task<string> GetNormalizedEmailAsync(AspNetMembershipUser user, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() => user.NormalizedEmail);
    }

    public Task<string> GetNormalizedUserNameAsync(AspNetMembershipUser user, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() => user.NormalizedUserName);
    }

    public Task<string> GetPasswordHashAsync(AspNetMembershipUser user, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() => user.PasswordHash);
    }

    public Task<string> GetUserIdAsync(AspNetMembershipUser user, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() => user.Id.ToString());
    }

    public Task<string> GetUserNameAsync(AspNetMembershipUser user, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() => user.UserName);
    }

    public Task<bool> HasPasswordAsync(AspNetMembershipUser user, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() => !string.IsNullOrEmpty(user.PasswordHash));
    }

    public Task SetEmailAsync(AspNetMembershipUser user, string email, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() => user.Email = email);
    }

    public Task SetEmailConfirmedAsync(AspNetMembershipUser user, bool confirmed, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() => user.EmailConfirmed = confirmed);
    }

    public Task SetNormalizedEmailAsync(AspNetMembershipUser user, string normalizedEmail, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() => user.NormalizedEmail = normalizedEmail);
    }

    public Task SetNormalizedUserNameAsync(AspNetMembershipUser user, string normalizedName, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() => user.NormalizedUserName = normalizedName);
    }

    public Task SetPasswordHashAsync(AspNetMembershipUser user, string passwordHash, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() => user.PasswordHash = passwordHash);
    }

    public Task SetUserNameAsync(AspNetMembershipUser user, string userName, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() => user.UserName = userName);
    }

    public Task<IdentityResult> UpdateAsync(AspNetMembershipUser user, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() =>
        {
            try
            {
                User dbUser = _dbcontext.Users
                    .Include(u => u.AspNetUsers).ThenInclude(u => u.AspNetMembership)
                    .Include(u => u.AspNetUsers).ThenInclude(u => u.AspNetApplication)
                    .Include(u => u.UserGroups)
                    .SingleOrDefault(u => u.UserId.ToString() == user.Id);

                if (dbUser != null)
                {
                    this.Convert(user, dbUser);
                    _dbcontext.Users.Update(dbUser);
                    _dbcontext.SaveChanges();
                }
                return IdentityResult.Success;
            }
            catch(Exception ex)
            {
                return IdentityResult.Failed(new IdentityError
                {
                    Code = ex.GetType().Name,
                    Description = ex.Message
                });
            }
        });
    }

    private void Convert(User from, AspNetMembershipUser to)
    {
        to.Id = from.ProviderUserKey.ToString();
        to.UserName = from.ProviderUserName;
        to.NormalizedUserName = from.ProviderUserName.ToLower();
        to.Email = from.ProviderEmailAddress;
        to.NormalizedEmail = from.ProviderEmailAddress.ToLower();
        to.EmailConfirmed = true;
        to.PasswordHash = from.AspNetUser.AspNetMembership.Password;
        to.PasswordSalt = from.AspNetUser.AspNetMembership.PasswordSalt;
        to.PasswordFormat = from.AspNetUser.AspNetMembership.PasswordFormat;
        to.AccessFailedCount = from.AspNetUser.AspNetMembership.FailedPasswordAttemptCount;
        to.EmailConfirmed = true;
        to.Roles.Clear();
        from.UserGroups.ToList().ForEach(ug =>
        {
            to.Roles.Add(new IdentityUserRole<string>
            {
                RoleId = ug.GroupId.ToString(),
                UserId = ug.UserId.ToString()
            });
        });
        to.PhoneNumber = from.Phone ?? from.ShippingPhone;
        to.PhoneNumberConfirmed = !string.IsNullOrEmpty(to.PhoneNumber);
        to.SecurityStamp = from.AspNetUser.AspNetMembership.PasswordSalt;
    }

    private void Convert(AspNetMembershipUser from , User to)
    {
        AspNetApplication application = _dbcontext.AspNetApplications.First();

        to.ProviderUserKey = Guid.Parse(from.Id);
        to.ProviderUserName = from.UserName;
        to.ProviderEmailAddress = from.Email;
        to.InternalEmail = $"c_{Guid.NewGuid().ToString()}@mycompany.com";
        to.AccountOwner = "MYCOMPANY";
        to.UserStatusId = (int)UserStatus.Normal;

        AspNetUser aspNetUser = to.AspNetUser;

        if (to.AspNetUser == null)
        {
            to.AspNetUser = new AspNetUser
            {
                ApplicationId = application.ApplicationId,
                AspNetApplication= application,
                AspNetMembership = new AspNetMembership
                {
                    ApplicationId = application.ApplicationId,
                    AspNetApplication = application
                }
            };
        }

        to.AspNetUser.UserId = Guid.Parse(from.Id);
        to.AspNetUser.UserName = from.UserName;
        to.AspNetUser.LoweredUserName = from.UserName.ToLower();
        to.AspNetUser.LastActivityDate = DateTime.UtcNow;
        to.AspNetUser.IsAnonymous = false;
        to.AspNetUser.ApplicationId = application.ApplicationId;
        to.AspNetUser.AspNetMembership.CreateDate = DateTime.UtcNow;
        to.AspNetUser.AspNetMembership.Email = from.Email;
        to.AspNetUser.AspNetMembership.IsApproved = true;
        to.AspNetUser.AspNetMembership.LastLoginDate = DateTime.Parse("1754-01-01 00:00:00.000");
        to.AspNetUser.AspNetMembership.LastLockoutDate = DateTime.Parse("1754-01-01 00:00:00.000");
        to.AspNetUser.AspNetMembership.LastPasswordChangedDate = DateTime.Parse("1754-01-01 00:00:00.000");
        to.AspNetUser.AspNetMembership.LoweredEmail = from.NormalizedEmail.ToLower();
        to.AspNetUser.AspNetMembership.Password = from.PasswordHash;
        to.AspNetUser.AspNetMembership.PasswordSalt = from.PasswordSalt;
        to.AspNetUser.AspNetMembership.PasswordFormat = from.PasswordFormat;
        to.AspNetUser.AspNetMembership.IsLockedOut = false;
        to.AspNetUser.AspNetMembership.FailedPasswordAnswerAttemptWindowStart = DateTime.Parse("1754-01-01 00:00:00.000");
        to.AspNetUser.AspNetMembership.FailedPasswordAttemptWindowStart = DateTime.Parse("1754-01-01 00:00:00.000");

        // Merge Groups/Roles
        to.UserGroups
            .Where(ug => !from.Roles.Any(r => ug.GroupId.ToString() == r.RoleId))
            .ToList()
            .ForEach(ug => to.UserGroups.Remove(ug));

        to.UserGroups
            .Join(from.Roles, ug => ug.GroupId.ToString(), r => r.RoleId, (ug, r) => new { To = ug, From = r })
            .ToList()
            .ForEach(j =>
            {
                j.To.UserId = long.Parse(j.From.UserId);
                j.To.GroupId = int.Parse(j.From.RoleId);
            });

        from.Roles
            .Where(r => !to.UserGroups.Any(ug => ug.GroupId.ToString() == r.RoleId))
            .ToList()
            .ForEach(r =>
            {
                to.UserGroups.Add(new UserGroup
                {
                    UserId = long.Parse(from.Id),
                    GroupId = int.Parse(r.RoleId)
                });
            });
    }
}

AspNetMembershipPasswordHasher。反恐精英

public class AspNetMembershipPasswordHasher : IPasswordHasher<AspNetMembershipUser>
{
    private readonly int _saltSize;
    private readonly int _bytesRequired;
    private readonly int _iterations;

    public AspNetMembershipPasswordHasher()
    {
        this._saltSize = 128 / 8;
        this._bytesRequired = 32;
        this._iterations = 1000;
    }

    public string HashPassword(AspNetMembershipUser user, string password)
    {
        string passwordHash = null;
        string passwordSalt = null;

        this.HashPassword(password, out passwordHash, ref passwordSalt);

        user.PasswordSalt = passwordSalt;
        return passwordHash;
    }

    public PasswordVerificationResult VerifyHashedPassword(AspNetMembershipUser user, string hashedPassword, string providedPassword)
    {
        // Throw an error if any of our passwords are null
        if (hashedPassword == null)
        {
            throw new ArgumentNullException("hashedPassword");
        }

        if (providedPassword == null)
        {
            throw new ArgumentNullException("providedPassword");
        }

        string providedPasswordHash = null;

        if (user.PasswordFormat == 0)
        {
            providedPasswordHash = providedPassword;
        }
        else if (user.PasswordFormat == 1)
        {

            string providedPasswordSalt = user.PasswordSalt;

            this.HashPassword(providedPassword, out providedPasswordHash, ref providedPasswordSalt);
        }
        else
        {
            throw new NotSupportedException("Encrypted passwords are not supported.");
        }

        if (providedPasswordHash == hashedPassword)
        {
            return PasswordVerificationResult.Success;
        }
        else
        {
            return PasswordVerificationResult.Failed;
        }
    }

    private void HashPassword(string password, out string passwordHash, ref string passwordSalt)
    {
        byte[] hashBytes = null;
        byte[] saltBytes = null;
        byte[] totalBytes = new byte[this._saltSize + this._bytesRequired];

        if (!string.IsNullOrEmpty(passwordSalt))
        {
            // Using existing salt.
            using (var pbkdf2 = new Rfc2898DeriveBytes(password, Convert.FromBase64String(passwordSalt), this._iterations))
            {
                saltBytes = pbkdf2.Salt;
                hashBytes = pbkdf2.GetBytes(this._bytesRequired);
            }
        }
        else
        {
            // Generate a new salt.
            using (var pbkdf2 = new Rfc2898DeriveBytes(password, this._saltSize, this._iterations))
            {
                saltBytes = pbkdf2.Salt;
                hashBytes = pbkdf2.GetBytes(this._bytesRequired);
            }
        }

        Buffer.BlockCopy(saltBytes, 0, totalBytes, 0, this._saltSize);
        Buffer.BlockCopy(hashBytes, 0, totalBytes, this._saltSize, this._bytesRequired);

        using (SHA256 hashAlgorithm = SHA256.Create())
        {
            passwordHash = Convert.ToBase64String(hashAlgorithm.ComputeHash(totalBytes));
            passwordSalt = Convert.ToBase64String(saltBytes);
        }
    }
}

共有1个答案

段干飞翔
2023-03-14

我的一位同事能够帮助我。下面是哈希函数的样子。通过此更改,ASP. NET Idity能够利用现有的ASP. NET Manny数据库。

private void HashPassword(string password, out string passwordHash, ref string passwordSalt)
    {
        byte[] passwordBytes = Encoding.Unicode.GetBytes(password);
        byte[] saltBytes = null;

        if (!string.IsNullOrEmpty(passwordSalt))
        {
            saltBytes = Convert.FromBase64String(passwordSalt);
        }
        else
        {
            saltBytes = new byte[128 / 8];
            using (var rng = RandomNumberGenerator.Create())
            {
                rng.GetBytes(saltBytes);
            }
        }

        byte[] totalBytes = new byte[saltBytes.Length + passwordBytes.Length];
        Buffer.BlockCopy(saltBytes, 0, totalBytes, 0, saltBytes.Length);
        Buffer.BlockCopy(passwordBytes, 0, totalBytes, saltBytes.Length, passwordBytes.Length);

        using (SHA1 hashAlgorithm = SHA1.Create())
        {
            passwordHash = Convert.ToBase64String(hashAlgorithm.ComputeHash(totalBytes));
        }

        passwordSalt = Convert.ToBase64String(saltBytes);
    }

您可以在GitHib上找到所有源代码。

 类似资料:
  • 问题内容: 我是AngularJS的新手,正在尝试为我的新Web应用程序对其进行评估。 需求: 我将有一个ASP.NET Web API,它将从Android,iPhone和Web应用程序(ASP.NET MVC)中使用。ASP.NET Identity将在Web API中实现。这三个应用程序都将调用Web API的login方法来获取auth令牌。 问题: 我的问题是当Angular进行调用以获

  • 我在ASP.NET中使用窗体身份验证。我已将超时设置为1分钟。一旦我登录,如果经过身份验证,我将被重定向到主页(homepage.aspx)。这很好用。如果我远离网站,1分钟后回来,并试图访问任何其他页面,我将按预期重定向到登录页面。我的问题是,如果我回来做一些回发或刷新,那么只有我被重定向到登录页面,否则我将停留在同一个页面。如果我在1分钟后回来,我应该做什么才能登录页面出现在屏幕上。

  • 我有一个传统的ASP.NET应用程序,我想把它移到ASP.NET5(vNext)。我这样做是为了学习。

  • 问题内容: 默认情况下,ASP.NET Identity用户数据存储在mdf文件中。 我想将数据存储在Sql数据库中,以便将默认连接字符串更改为基于EF的连接: 现在,我要注册用户后立即收到错误消息。 我使用VS2013的默认MVC5项目模板。 问题答案: 请尝试以以下格式指定连接字符串: 然后确保在Models / IdentityModels.cs中

  • 本文向大家介绍asp.net mvc中Forms身份验证身份验证流程,包括了asp.net mvc中Forms身份验证身份验证流程的使用技巧和注意事项,需要的朋友参考一下 验证流程 一、用户登录 1、验证表单:ModelState.IsValid 2、验证用户名和密码:通过查询数据库验证 3、如果用户名和密码正确,则在客户端保存Cookie以保存用户登录状态:SetAuthCookie     1

  • null 这在2.0中可以使用,但如果令牌无效(上面的步骤2)并且声明从未添加,我会得到“no authenticationScheme was specified,and there was no DefaultChallengeScheme found”。 所以现在我读到了2.0版本中身份验证的改变: https://docs.microsoft.com/en-us/aspnet/core/m