gpt4 book ai didi

c# - 如何在 ASP.NET Identity 中使用和使用 ASP.NET Membership 数据库?

转载 作者:行者123 更新时间:2023-11-30 14:23:20 28 4
gpt4 key购买 nike

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

我目前有一个概念证明,涉及一个网络 API、身份服务器和一个 MVC 网络应用程序作为我的客户端。身份服务器实现了 IdentityUser 的子类并实现了 IUserStore/IUserPasswordStore/IUserEmailStore 以使其适应我现有数据库中的 ASP.NET 成员表。我可以注册新用户并通过我的 POC MVC 客户端应用程序登录,但这些用户无法登录我的旧应用程序。相反,在遗留应用程序中注册的用户无法登录到我的 POC MVC 客户端。我认为这是因为我的 IPasswordHasher 实现没有像我的遗留应用程序中的 ASP.NET 成员一样对密码进行哈希处理。

下面是我的代码。任何对我可能做错了什么的洞察力将不胜感激。安全和密码学不是我的强项。

Startup.cs

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.cs

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

AspNetMembershipUserStore.cs

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.cs

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);
}
}
}

最佳答案

我的一位同事能够帮助我。下面是散列函数的样子。通过此更改,ASP.NET Identity 能够利用现有的 ASP.NET 成员资格数据库。

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);
}

您可以找到所有 source code on GitHib .

关于c# - 如何在 ASP.NET Identity 中使用和使用 ASP.NET Membership 数据库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45873006/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com