Asp.Net.Identity为何物请自行搜索,也可转向此文章http://www.cnblogs.com/shanyou/p/3918178.html
本来微软已经帮我们将授权、认证以及数据库存储都一一处理好了。但是总有这种情况,如我们现在的项目是已经存在了数据库,且库里已经有用户、角色等信息表,但是
我们还是贪心想使用微软的授权、认证类库。这里我就来实际实践下到底可行不可行~
第一步、新建一个Asp.Net MVC框架的web工程
第二部、Nuget上安装Microsoft.AspNet.Identity、Microsoft.AspNet.Identity.Owin
其中Microsoft.AspNet.Identity.Owin有依赖项,它依赖了这几个包:
Microsoft.Owin.Security.OAuth MSDN注解:包含与 OAuth 提供程序相关的类型。(详细信息参考 https://msdn.microsoft.com/zh-cn/library/microsoft.owin.security.oauth(v=vs.111).aspx)
Microsoft.Owin.Security.Cookies MSDN注解:提供与身份 cookie 相关的类型。 (详细信息参考 https://msdn.microsoft.com/zh-cn/library/microsoft.owin.security.cookies(v=vs.111).aspx)
Microsoft.Owin.Security MSDN注解:包含与身份验证相关的类型。 (详细信息参考 https://msdn.microsoft.com/zh-cn/library/microsoft.owin.security(v=vs.111).aspx)
Microsoft.AspNet.Identity.Core MSDN注解:包含与管理 ASP.NET Identity 的用户和角色相关的类和接口。
(信息信息参考:https://msdn.microsoft.com/library/microsoft.aspnet.identity(v=vs.111).aspx)
从MSDN的注解可以看出来Microsoft.AspNet.Identity.Owin里其实就是将网站的登录、注册业务场景所需的API进行了封装;
第三部、建模
如我现在的数据库的用户表为BASE_USER,表结构如下
class="brush:csharp;collapse:true;;gutter:false;">CREATE TABLE [dbo].[BASE_USER]( [ID] [uniqueidentifier] NOT NULL PRIMARY KEY, [NAME] [varchar](50) NOT NULL, [PWD] [varchar](50) NOT NULL, ) ON [PRIMARY]
我们在工程站点的Models文件夹里新建一个BASE_USER类,让它继承Microsoft.AspNet.Identity.IUser<GUID>,这里我们加一个数据表不存在的NICKNAME昵称字段,到后面看看会有什么效果~
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 6 namespace IdeintityDemo.Models 7 { 8 public class BASE_USER : Microsoft.AspNet.Identity.IUser<Guid> 9 { 10 /// <summary> 11 /// 用户编号 12 /// </summary> 13 public Guid Id { get; set; } 14 /// <summary> 15 /// 用户名 16 /// </summary> 17 public string UserName { get; set; } 18 /// <summary> 19 /// 密码 20 /// </summary> 21 public string PWD { get; set; } 22 /// <summary> 23 /// 昵称 24 /// </summary> 25 public string NickName { get; set; } 26 27 public bool RememberMe { get; set; } 28 } 29 }View Code
第四部 创建UserStore类,该类通过继承接口IUserStore来实现用户存储在数据库的api
1 using Microsoft.AspNet.Identity; 2 using System; 3 using System.Collections.Generic; 4 using System.Data; 5 using System.Data.SqlClient; 6 using System.Linq; 7 using System.Threading.Tasks; 8 using System.Web; 9 using System.Security.Claims; 10 using IdeintityDemo.Models; 11 using IdeintityDemo.Common; 12 13 namespace IdeintityDemo.Identity 14 { 15 16 /// <summary> 17 /// 用户持久化存储对象 18 /// 必须实现Microsoft.AspNet.Identity相应接口,否则在SignInManager类进行登录校验过程中 19 /// 会弹出未实现相关接口的异常! 20 /// IUserStore:检测是否存在账户 21 /// IUserPasswordStore:校验密码 22 /// IUserLockoutStore:锁定账户相关操作 23 /// IUserClaimStore:存储用户特定的声明 24 /// IUserEmailStore:邮箱关联、验证等相关操作 25 /// IUserPhoneNumberStore:手机关联、验证相关操作 26 /// IUserTwoFactorStore:获取或设置用户双重身份验证的方法。 27 /// </summary> 28 public class HsUserStore : Microsoft.AspNet.Identity.IUserStore<BASE_USER, Guid>, 29 Microsoft.AspNet.Identity.IUserPasswordStore<BASE_USER, Guid>, 30 Microsoft.AspNet.Identity.IUserLockoutStore<BASE_USER, Guid>, 31 Microsoft.AspNet.Identity.IUserClaimStore<BASE_USER, Guid>, 32 Microsoft.AspNet.Identity.IUserEmailStore<BASE_USER, Guid>, 33 Microsoft.AspNet.Identity.IUserPhoneNumberStore<BASE_USER, Guid>, 34 Microsoft.AspNet.Identity.IUserTwoFactorStore<BASE_USER, Guid> 35 { 36 37 /// <summary> 38 /// 声明 39 /// </summary> 40 public IList<System.Security.Claims.Claim> Claims = null; 41 /// <summary> 42 /// 用户 43 /// </summary> 44 public BASE_USER UserIdentity = null; 45 46 /// <summary> 47 /// 实例化 48 /// </summary> 49 public HsUserStore() 50 { 51 //声明 52 Claims = new List<System.Security.Claims.Claim>(); 53 } 54 /// <summary> 55 /// 创建用户 56 /// </summary> 57 /// <param name="user"></param> 58 /// <returns></returns> 59 public Task CreateAsync(BASE_USER user) 60 { 61 return Task.Run(() => { 62 string sql = @"INSERT INTO [dbo].[BASE_USER]([ID],[NAME],[PWD]) 63 VALUES(@UserID,@name,@pwd)"; 64 SqlParameter[] parameters = { 65 new SqlParameter("@UserID", Guid.NewGuid()), 66 new SqlParameter("@name", user.UserName), 67 new SqlParameter("@pwd", user.PWD) 68 }; 69 int iResult = DbHelperSQL.ExecuteSql(sql, parameters); 70 }); 71 } 72 /// <summary> 73 /// 删除用户 74 /// </summary> 75 /// <param name="user"></param> 76 /// <returns></returns> 77 public Task DeleteAsync(BASE_USER user) 78 { 79 return Task.Run(() => { 80 string sql = @"DELETE FROM [dbo].[BASE_USER] WHERE ID=@ID"; 81 SqlParameter[] parameters = { 82 new SqlParameter("@UserID", user.Id)}; 83 int iResult = DbHelperSQL.ExecuteSql(sql, parameters); 84 }); 85 } 86 /// <summary> 87 /// 根据用户id获取用户 88 /// </summary> 89 /// <param name="userId"></param> 90 /// <returns></returns> 91 public Task<BASE_USER> FindByIdAsync(Guid userId) 92 { 93 return Task<BASE_USER>.Run(() => 94 { 95 BASE_USER result = new BASE_USER(); 96 string sql = @"SELECT * FROM [dbo].[BASE_USER] WHERE ID=@ID"; 97 SqlParameter[] parameters = { 98 new SqlParameter("@ID", userId)}; 99 DataSet ds = DbHelperSQL.Query(sql, parameters); 100 if (ds == null || ds.Tables == null || ds.Tables[0].Rows.Count <= 0) 101 return result; 102 //model 103 DataRow dr = ds.Tables[0].Rows[0]; 104 result.Id = Guid.Parse(dr["ID"].ToString()); 105 result.UserName = dr["NAME"].ToString(); 106 result.PWD = dr["PWD"].ToString(); 107 return result; 108 }); 109 } 110 /// <summary> 111 /// 根据名称获取用户信息 112 /// </summary> 113 /// <param name="userName"></param> 114 /// <returns></returns> 115 public Task<BASE_USER> FindByNameAsync(string userName) 116 { 117 return Task<BASE_USER>.Run(() => 118 { 119 BASE_USER result = new BASE_USER(); 120 string sql = @"SELECT * FROM [dbo].[BASE_USER] WHERE NAME=@NAME"; 121 SqlParameter[] parameters = { 122 new SqlParameter("@NAME", userName)}; 123 DataSet ds = DbHelperSQL.Query(sql, parameters); 124 if (ds == null || ds.Tables == null || ds.Tables[0].Rows.Count <= 0) 125 return result; 126 //model 127 DataRow dr = ds.Tables[0].Rows[0]; 128 result.Id = Guid.Parse(dr["ID"].ToString()); 129 result.UserName = dr["NAME"].ToString(); 130 result.PWD = dr["PWD"].ToString(); 131 132 return result; 133 }); 134 } 135 /// <summary> 136 /// 更新用户 137 /// </summary> 138 /// <param name="user"></param> 139 /// <returns></returns> 140 public Task UpdateAsync(BASE_USER user) 141 { 142 return Task.Run(() => 143 { 144 //省略... 145 }); 146 } 147 /// <summary> 148 /// 异步返回当前失败的访问尝试次数。当密码被验证或帐户被锁定时,这个数字通常会被重置。 149 /// (这里因为我数据库里没有去做这一块的记录保存,所以先写死返回1) 150 /// </summary> 151 /// <param name="user">用户</param> 152 /// <returns></returns> 153 public Task<int> GetAccessFailedCountAsync(BASE_USER user) 154 { 155 return Task.FromResult<int>(1); 156 } 157 /// <summary> 158 /// 获取锁定状态 159 /// </summary> 160 /// <param name="user"></param> 161 /// <returns></returns> 162 public Task<bool> GetLockoutEnabledAsync(BASE_USER user) 163 { 164 return Task.FromResult<bool>(false); 165 } 166 /// <summary> 167 /// 获取锁定结束时间 168 /// </summary> 169 /// <param name="user"></param> 170 /// <returns></returns> 171 public Task<DateTimeOffset> GetLockoutEndDateAsync(BASE_USER user) 172 { 173 throw new NotImplementedException(); 174 } 175 /// <summary> 176 /// 记录试图访问用户失败的记录。 177 /// </summary> 178 /// <param name="user"></param> 179 /// <returns></returns> 180 public Task<int> IncrementAccessFailedCountAsync(BASE_USER user) 181 { 182 return Task.FromResult<int>(1); 183 } 184 /// <summary> 185 /// 重置访问失败计数,通常在帐户成功访问之后 186 /// </summary> 187 /// <param name="user"></param> 188 /// <returns></returns> 189 public Task ResetAccessFailedCountAsync(BASE_USER user) 190 { 191 return Task.FromResult(false); 192 } 193 /// <summary> 194 /// 异步设置是否可以锁定用户。 195 /// </summary> 196 /// <param name="user"></param> 197 /// <param name="enabled"></param> 198 /// <returns></returns> 199 public Task SetLockoutEnabledAsync(BASE_USER user, bool enabled) 200 { 201 return Task.Run(() => { }); 202 } 203 /// <summary> 204 /// 异步锁定用户直到指定的结束日期 205 /// </summary> 206 /// <param name="user"></param> 207 /// <param name="lockoutEnd"></param> 208 /// <returns></returns> 209 public Task SetLockoutEndDateAsync(BASE_USER user, DateTimeOffset lockoutEnd) 210 { 211 return Task.Run(() => 212 { 213 214 }); 215 } 216 /// <summary> 217 /// 获取用户密码 218 /// </summary> 219 /// <param name="user"></param> 220 /// <returns></returns> 221 public Task<string> GetPasswordHashAsync(BASE_USER user) 222 { 223 return Task<string>.Run(() => 224 { 225 return user.PWD; 226 }); 227 } 228 /// <summary> 229 /// 是否有密码 230 /// </summary> 231 /// <param name="user"></param> 232 /// <returns></returns> 233 public Task<bool> HasPasswordAsync(BASE_USER user) 234 { 235 return Task.FromResult<bool>(!string.IsNullOrEmpty(user.PWD)); 236 } 237 /// <summary> 238 /// 密码进行加密 239 /// </summary> 240 /// <param name="user"></param> 241 /// <param name="passwordHash"></param> 242 /// <returns></returns> 243 public Task SetPasswordHashAsync(BASE_USER user, string passwordHash) 244 { 245 return Task.Run(() => 246 { 247 user.PWD = passwordHash;//加密后 248 }); 249 } 250 /// <summary> 251 /// 添加一个声明 252 /// </summary> 253 /// <param name="user"></param> 254 /// <param name="claim"></param> 255 /// <returns></returns> 256 public Task AddClaimAsync(BASE_USER user, Claim claim) 257 { 258 return Task.Run(() => { Claims.Add(claim); }); 259 } 260 /// <summary> 261 /// 获取改用户的所有声明 262 /// </summary> 263 /// <param name="user"></param> 264 /// <returns></returns> 265 public Task<IList<Claim>> GetClaimsAsync(BASE_USER user) 266 { 267 return Task.Run<IList<System.Security.Claims.Claim>>(() => 268 { 269 IList<System.Security.Claims.Claim> list = new List<System.Security.Claims.Claim>(); 270 return list; 271 }); 272 } 273 /// <summary> 274 /// 移除申明 275 /// </summary> 276 /// <param name="user"></param> 277 /// <param name="claim"></param> 278 /// <returns></returns> 279 public Task RemoveClaimAsync(BASE_USER user, Claim claim) 280 { 281 return Task.Run(() => 282 { 283 284 }); 285 } 286 /// <summary> 287 /// 通过邮箱获取对应的用户信息 288 /// </summary> 289 /// <param name="email"></param> 290 /// <returns></returns> 291 public Task<BASE_USER> FindByEmailAsync(string email) 292 { 293 return Task<BASE_USER>.Run(() => new BASE_USER()); 294 } 295 /// <summary> 296 /// 获取邮箱 297 /// </summary> 298 /// <param name="user"></param> 299 /// <returns></returns> 300 public Task<string> GetEmailAsync(BASE_USER user) 301 { 302 return Task<string>.Run(() => string.Empty); 303 } 304 /// <summary> 305 /// 确认邮箱 306 /// </summary> 307 /// <param name="user"></param> 308 /// <returns></returns> 309 public Task<bool> GetEmailConfirmedAsync(BASE_USER user) 310 { 311 return Task.FromResult<bool>(true); 312 } 313 /// <summary> 314 /// 修改邮箱 315 /// </summary> 316 /// <param name="user"></param> 317 /// <param name="email"></param> 318 /// <returns></returns> 319 public Task SetEmailAsync(BASE_USER user, string email) 320 { 321 return Task.Run(() => { }); 322 } 323 /// <summary> 324 ///设置用户是否邮箱确认 325 /// </summary> 326 /// <param name="user"></param> 327 /// <param name="confirmed"></param> 328 /// <returns></returns> 329 public Task SetEmailConfirmedAsync(BASE_USER user, bool confirmed) 330 { 331 throw new NotImplementedException(); 332 } 333 /// <summary> 334 /// 获取联系电话 335 /// </summary> 336 /// <param name="user"></param> 337 /// <returns></returns> 338 public Task<string> GetPhoneNumberAsync(BASE_USER user) 339 { 340 return Task.FromResult<string>(string.Empty); 341 } 342 /// <summary> 343 /// 获取用户电话号码是否已确认 344 /// </summary> 345 /// <param name="user"></param> 346 /// <returns></returns> 347 public Task<bool> GetPhoneNumberConfirmedAsync(BASE_USER user) 348 { 349 return Task.FromResult<bool>(true); 350 } 351 /// <summary> 352 /// 设置用户电话号码 353 /// </summary> 354 /// <param name="user"></param> 355 /// <param name="phoneNumber"></param> 356 /// <returns></returns> 357 public Task SetPhoneNumberAsync(BASE_USER user, string phoneNumber) 358 { 359 return Task.Run(() => { }); 360 } 361 /// <summary> 362 /// 设置与用户关联的电话号码 363 /// </summary> 364 /// <param name="user"></param> 365 /// <param name="confirmed"></param> 366 /// <returns></returns> 367 public Task SetPhoneNumberConfirmedAsync(BASE_USER user, bool confirmed) 368 { 369 return Task.Run(() => { }); 370 } 371 /// <summary> 372 /// 是否为用户启用了双重身份验证。 373 /// </summary> 374 /// <param name="user"></param> 375 /// <returns></returns> 376 public Task<bool> GetTwoFactorEnabledAsync(BASE_USER user) 377 { 378 return Task.FromResult<bool>(false); 379 } 380 /// <summary> 381 /// 设置双重身份验证 382 /// </summary> 383 /// <param name="user"></param> 384 /// <param name="enabled"></param> 385 /// <returns></returns> 386 public Task SetTwoFactorEnabledAsync(BASE_USER user, bool enabled) 387 { 388 throw new NotImplementedException(); 389 } 390 /// <summary> 391 /// 释放 392 /// </summary> 393 public void Dispose() 394 { 395 throw new NotImplementedException(); 396 } 397 398 } 399 }View Code
第五步继承UserManager类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using IdeintityDemo.Models; 6 using Microsoft.AspNet.Identity; 7 8 namespace IdeintityDemo.Identity 9 { 10 public class HsUserManager:UserManager<BASE_USER,Guid> 11 { 12 /// <summary> 13 /// 通过构造函数注入用户存储实现类 14 /// </summary> 15 /// <param name="store"></param> 16 public HsUserManager(HsUserStore store) : base(store) 17 { 18 19 } 20 } 21 }View Code
上面的代码特别特别简单,但是确很重要,你就理解为将上面定义的HsUserStore注入到一个IOC容器里就好了。 如果不注入,你对用户的所有读写db操作都没有,后续整个登录、注册、验证业务都无法实施!
第六步继承SignInManager 类
SignInManager类是Microsoft.AspNet.Identity.Owin命名空间下的,集成了用户登录进行管理的相关API,是Asp.Net.Identity里必不可少的处理类
我们需要自定义一个登录管理类,继承SignInManager。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using IdeintityDemo.Models; 6 using Microsoft.AspNet.Identity; 7 using Microsoft.AspNet.Identity.Owin; 8 using Microsoft.Owin; 9 using System.Threading.Tasks; 10 using Microsoft.Owin.Security; 11 12 namespace IdeintityDemo.Identity 13 { 14 /// <summary> 15 /// 登录管理,此处用到了UserManager 16 /// </summary> 17 public class HsSignInManager : SignInManager<BASE_USER, Guid> 18 { 19 20 /// <summary> 21 /// 构造函数 22 /// </summary> 23 /// <param name="UserManager"></param> 24 /// <param name="AuthenticationManager"></param> 25 public HsSignInManager(Microsoft.AspNet.Identity.UserManager<BASE_USER, Guid> UserManager, Microsoft.Owin.Security.IAuthenticationManager AuthenticationManager) 26 : base(UserManager, AuthenticationManager) 27 { 28 29 } 30 31 /// <summary> 32 /// 根据用户名密码,验证用户登录 33 /// </summary> 34 /// <param name="userName"></param> 35 /// <param name="password"></param> 36 /// <param name="isPersistent"></param> 37 /// <param name="shouldLockout"></param> 38 /// <returns></returns> 39 public override System.Threading.Tasks.Task<Microsoft.AspNet.Identity.Owin.SignInStatus> PasswordSignInAsync(string userName, 40 string password, 41 bool isPersistent, 42 bool shouldLockout) 43 { 44 /*这里可以直接通过PasswordSignInAsync来校验,也可以重写~ */ 45 //这里用Find方法会返回空的user。。。搞不懂。。 46 var user = base.UserManager.FindByName<BASE_USER, Guid>(userName); 47 if (user == null || user.Id == Guid.Empty) 48 return Task.FromResult<SignInStatus>(SignInStatus.Failure); 49 else if (user.PWD != password) 50 return Task.FromResult<SignInStatus>(SignInStatus.Failure); 51 else 52 { 53 /*这个时候如果不写入到cooks里,在Home控制器的Index action里会被系统的 54 Authorize刷选器拦截*/ 55 // 利用ASP.NET Identity获取identity 对象 56 var identity = base.UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); 57 // 将上面拿到的identity对象登录 58 base.AuthenticationManager.SignIn(new AuthenticationProperties() 59 { IsPersistent = true }, identity.Result); 60 return Task.FromResult<SignInStatus>(SignInStatus.Success); 61 } 62 /*这里如果你想直接使用微软的登入方法也可以,直接base.就ok啦*/ 63 //return base.PasswordSignInAsync(userName, 64 // password, 65 // isPersistent, 66 // shouldLockout); 67 } 68 69 70 } 71 }View Code
上面最重要的方法就是PasswordSignInAsync,这个方法就是登入方法。
可以说我们封装的工作已经完成了,封装了三个类
现在我们看Controller里的代码是怎么写的吧~
这是注册的Action,所属Controller当然是AccountController啦。。
1 /// <summary> 2 /// 注册 3 /// </summary> 4 /// <returns></returns> 5 [HttpPost] 6 [AllowAnonymous] 7 public async Task<ActionResult> Register(BASE_USER user) 8 { 9 Microsoft.Owin.IOwinContext OwinContext = HttpContext.GetOwinContext(); 10 //用户储存 11 HsUserStore userStore = new HsUserStore(); 12 //UserManager 13 HsUserManager UserManager = new HsUserManager(userStore); 14 IdentityResult result = await UserManager.CreateAsync(user); 15 if (result.Succeeded) 16 { 17 Response.Write("注册成功!"); 18 return RedirectToAction("index", "home"); 19 } 20 return View(); 21 } 22 23 [AllowAnonymous] 24 public ActionResult Register() 25 { 26 return View(); 27 }View Code
接下来是登录的Action代码~
1 [AllowAnonymous] 2 public ActionResult Login() 3 { 4 return View(); 5 } 6 7 8 [HttpPost] 9 [AllowAnonymous] 10 public async Task<ActionResult> Login(BASE_USER user) 11 { 12 if (string.IsNullOrEmpty(user.UserName)) { return View(); } 13 if (string.IsNullOrEmpty(user.PWD)) { return View(); } 14 //Context 15 Microsoft.Owin.IOwinContext OwinContext = HttpContext.GetOwinContext(); 16 //实例化UserStore对象 17 HsUserStore userStore = new HsUserStore(); 18 //UserManager 19 HsUserManager userManager = new HsUserManager(userStore); 20 //授权对象 21 IAuthenticationManager autherticationManager = OwinContext.Authentication; 22 //登录管理对象 23 HsSignInManager signManager = new HsSignInManager(userManager, autherticationManager); 24 25 //登录 26 Microsoft.AspNet.Identity.Owin.SignInStatus SignInStatus = Microsoft.AspNet.Identity.Owin.SignInStatus.Failure; 27 try 28 { 29 SignInStatus = await signManager.PasswordSignInAsync(user.UserName, 30 user.PWD, 31 true, 32 shouldLockout: false); 33 34 }catch(Exception ea) 35 { 36 37 } 38 //登录状态 39 switch (SignInStatus) 40 { 41 //成功 同一个Control里跳转直接使用RecirectToAction(ActionName) 42 case Microsoft.AspNet.Identity.Owin.SignInStatus.Success: 43 //不同控制器使用RedirectToAction 44 return RedirectToAction("Index", "Home"); //可以直接跳到别的Controller. 45 //锁定 46 case Microsoft.AspNet.Identity.Owin.SignInStatus.LockedOut: 47 Response.Write("账户被锁定啦~~~!"); 48 break; 49 //失败 50 case Microsoft.AspNet.Identity.Owin.SignInStatus.Failure: 51 Response.Write("登录失败啦~~~!"); 52 break; 53 //要求验证 54 case Microsoft.AspNet.Identity.Owin.SignInStatus.RequiresVerification: 55 Response.Write("需要验证!"); 56 break; 57 58 } 59 return View(); 60 }View Code
我们看我们Mvc路由默认配置初始页面是Home控制器下的Index
这是Home控制器下的Index声明
F5运行试下
运行发现浏览器直接跳转到了登录页面。。
在我们输入账号密码后,跳转到了Index页面
然后我们用浏览器查看下cookie有什么变化~
发现整个业务完成后浏览器保存了名为_IdentityDemo的 一个存储项,整个就是在我们登入方法里执行的。
好啦,整个Identity认证不依赖EF已经实现了,几个核心点就是需要实现IUser接口以及各种Store。。然后将实现各种Store的类注入到UserManager构造函数里
等有时间再实现下Identity命名空间下的角色管理这一块吧。。。不得不说微软的接口真是封装的好啊~
这里感谢下开源中国的李朝强,我是看他博客再自己实践出来的~感谢感谢~
u