我有几个传统的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);
}
}
}
我的一位同事能够帮助我。下面是哈希函数的样子。通过此更改,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