因为我之前主要是做架构方向的,可以理解为这些都是所谓的“小弟”在做的功能,而我一个不小心就掉进了陷阱里去。说是陷阱吧,其实主要是因为自己不熟悉,毕竟mvc还是比较庞大的,即便你顺着它的思路去做,也未必能做得对。
默认情况下生成好的项目是已经使用推荐的EF6实现了整个登录流程。而我又希望自己可以掌控关键的验证逻辑,所以一路摸下去把 ApplicationUser、AppplicationUserManager、UserManager、UserStore都看了一遍,经过各种尝试,发现验证逻辑封装在Microsoft.AspNet.Identity.EntityFramework(这个是盲打的,已经回不去了,如果错了欢迎指正)。
找到了目标当然第一想法是功课他,随着不断的踩坑发现工作量异常的大,遂放弃。想从另外一个角度入手,因为UserManager才是核心,那我顺着这条线是否可以把他已经实现的EF那套开给剔除掉。
于是就有了下面的代码:
首先要改造Model,于是自己把 ApplicationUser、IdentityUser、UserStore都重写了。前面两个比较简单,重点是UserStore,要实现IUserStore<TUser>这个接口,这里是自行编写验证逻辑的转折点。
其次,Controller也进行的小改造,去掉了SignInManager等,只保留了UserManager,同时Startup.Auth里面的DbContext也换成了自己的。
最后把Name也一同存入了Cookie,方便layout里面要显示用户名。
核心代码如下:
1.IdentityUser 其实这个类可以跟 ApplicationUser合并的,我也是跟官方生成项目时的写法保留下来的。可能故意分开在ApplicationUser里面写的是逻辑,而IdentityUser是对属性的定义,这样会显得更清晰一点吧,虽然我也会这么做,也很热衷于这么做,但区别是我不会让ApplicationUser继承IdentityUser,而是写一个Business来实现方法做一个彻底的拆分。
public class IdentityUser : IUser<string> { public string Id { get; set; } public string Name { get; set; } public string Password { get; set; } public string Role { get; set; } public string UserName { get; set; } }
2.UserStore其实并没有多少借鉴的意义,因为这是很小学生的写法了,主要是为了突出UserStore的重要性。
public class UserStore : IUserStore<ApplicationUser> { public async Task CreateAsync(ApplicationUser user) { using (var dbContext = GDbContext.Create()) { dbContext.User.Add(new User() { UserName = user.UserName, Password = user.Password, Name = user.Name, Role = user.Role, }); await dbContext.SaveChangesAsync(); } } public Task DeleteAsync(ApplicationUser user) { throw new NotImplementedException(); } public void Dispose() { //nothing to do } public Task<ApplicationUser> FindByIdAsync(string userId) { int id = 0; if (!Int32.TryParse(userId, out id)) { throw new ArgumentException(nameof(userId)); } using (var dbContext = GDbContext.Create()) { var user = dbContext.User.SingleOrDefault(u => u.ID == id && !u.IsDelete); return Task.FromResult(new ApplicationUser() { Id = user.ID.ToString(), Name = user.Name, Password = user.Password, Role = user.Role, UserName = user.UserName }); } } public Task<ApplicationUser> FindByNameAsync(string userName) { using (var dbContext = GDbContext.Create()) { var user = dbContext.User.SingleOrDefault(u => u.UserName == userName && !u.IsDelete); return Task.FromResult(new ApplicationUser() { Id = user.ID.ToString(), Name = user.Name, Password = user.Password, Role = user.Role, UserName = user.UserName }); } } public Task UpdateAsync(ApplicationUser user) { throw new NotImplementedException(); } }
3.Startup.Auth ConfigureAuth方法进行了如下的改动,主要是修改 DbContext 的获取,以及SecurityStampValidator.OnvalidateIdentity这个方法的参数调整。