.net core Identity学习(一)注册登录

身份认证基本每个应用都会需要,从.net版本的form authentication大概就是利用HttpModule填充IPrinciple一个这样的过程,说起来也不算太懂。。

最近在看.net core的身份认证,结合一些网上的资源做一个总结。

背景

identity在.net 4.5时代就有了,.net core应该是很大程度上的重写了(毕竟两个完全不同的环境了)。微软自己的官方文档有一些工具性的介绍,但是太过于依赖vs,通过一些点选,可以容易的搭建出来简单的登录功能,但是自己想修改里面的一部分的时候恐怕就犯难了。

所以在网上找了从头搭建Identity的教程,跟着走了一遍,比利用vs工具稍微熟悉一些。

介绍

Identity基本功能包括用户管理(结合EF或者其他数据库),用户注册、校验等。

涉及到的关键数据结构主要是这两个:

  • IdentityUser:Identity自定义的一个特殊用户基类,利用Identity管理的用户都存储在这个结构中,并通过ORM持久化到数据库里
  • IdentityDbContext:继承自DbContext,Identity的数据库Context,添加了Identity需要的功能,管理Identity需要使用的各个表。实际使用上一般不会直接操纵它---它其实也不像常用的DbContext那样暴露出一些表的DbSet,实际使用时,往往是操纵Identity的下面两个结构:

Identity提供的作为用户管理的类:

  • UserManager:管理用户,注册、查找、找回密码等等,操作用户相关的信息。
  • SignInManager:提供用户登录、注销功能。

 

使用

记录一下Identity基本的使用方法

注册和初始化

分两部分

  1. 注册服务:
//配置密码强度,这里可以进行其他的配置
services.Configure<IdentityOptions>(options =>
{
	options.Password.RequireDigit = false;
	options.Password.RequiredLength = 6;
	options.Password.RequireLowercase = false;
	options.Password.RequireNonAlphanumeric = false;
	options.Password.RequireUppercase = false;
});
//配置数据库,Identity依赖这个数据库去持久化用户相关数据
services.AddDbContext<FaIdentityDbContext>(options =>
		options.UseSqlServer(_config.GetConnectionString("DefaultConnection"))
	);
//添加Identity的服务
services.AddIdentity<FaUser, IdentityRole>()
   .AddEntityFrameworkStores<FaIdentityDbContext>()
   .AddDefaultTokenProviders();

基本的配置记录在注释里,代码中的两个数据结构:

  1. FaIdentityDbContext是继承自IdentityDbContext,是我自定义的数据库context类
  2. FaUser是我定义的用户结构,继承自IdentityUser

这两个结构的继承关系是必须的。

 

使用

用户注册

[HttpPost]
public async Task<IActionResult> Register(string email, string password, string repassword)
{
	if (password != repassword)
	{
		ModelState.AddModelError(string.Empty, "Password don't match");
		return View();
	}
var newUser = new FaUser
{
	UserName = email,
	Email = email
};

var userCreationResult = await _userManager.CreateAsync(newUser, password);
if (!userCreationResult.Succeeded)
{
	foreach (var error in userCreationResult.Errors)
		ModelState.AddModelError(string.Empty, error.Description);
	return View();
}

var emailConfirmationToken = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);
var tokenVerificationUrl = Url.Action("VerifyEmail", "Account", new { id = newUser.Id, token = emailConfirmationToken }, Request.Scheme);

await _messageService.Send(email, "Verify your email", $"Click &lt;a href=\"{tokenVerificationUrl}\"&gt;here&lt;/a&gt; to verify your email");

return Content("Check your email for a verification link");

}

用户注册依靠UserManager类(通过DI获得),注册后,还可以要求用户通过邮箱验证。

这里通过UserManager生成了邮箱验证的token,附带在用户的验证连接中。

_messageService是发送邮件的一个抽象类,和Identity无关,所以这里略过。

public async Task<IActionResult> VerifyEmail(string id, string token)
{
	var user = await _userManager.FindByIdAsync(id);
	if (user == null)
		throw new InvalidOperationException();
var emailConfirmationResult = await _userManager.ConfirmEmailAsync(user, token);
if (!emailConfirmationResult.Succeeded)
	return Content(emailConfirmationResult.Errors.Select(error =&gt; error.Description).Aggregate((allErrors, error) =&gt; allErrors += ", " + error));

return Content("Email confirmed, you can now log in");

}

通过UserNamager验证邮箱,验证成功时,IdentityUser类中的EmailConfirmed属性会被更新(ConfigEmailAsync方法内部实现的)。

至此用户注册成功

重置密码

[HttpPost]
public async Task<IActionResult> ForgotPassword(string email)
{
	var user = await _userManager.FindByEmailAsync(email);
	if (user == null)
		return Content("Check your email for a password reset link");
var passwordResetToken = await _userManager.GeneratePasswordResetTokenAsync(user);
var passwordResetUrl = Url.Action("ResetPassword", "Account", new { email = user.Email, id = user.Id, token = passwordResetToken }, Request.Scheme);

await _messageService.Send(email, "Password reset", $"Click &lt;a href=\"" + passwordResetUrl + "\"&gt;here&lt;/a&gt; to reset your password");

return Content("Check your email for a password reset link");

}

通过用户邮箱,生成密码重置的token,并制作成链接发送到用户邮箱中。

[HttpPost]
public async Task<IActionResult> ResetPassword(string id, string token, string password, string repassword)
{
	var user = await _userManager.FindByIdAsync(id);
	if (user == null)
		throw new InvalidOperationException();
if (password != repassword)
{
	ModelState.AddModelError(string.Empty, "Passwords do not match");
	return View();
}

var resetPasswordResult = await _userManager.ResetPasswordAsync(user, token, password);
if (!resetPasswordResult.Succeeded)
{
	foreach (var error in resetPasswordResult.Errors)
		ModelState.AddModelError(string.Empty, error.Description);
	return View();
}

return Content("Password updated");

}

根据Token,使用UserManager的ResetPasswordAsync方法重置密码

登录和登出

登录和登出通过的SignInManager来操作

所谓的登录(SignIn),实际可理解为将用户信息经过处理(加密)后,写入cookie中,这样后续所有的请求都会携带这些信息。

与之相辅的认证(Authenticate),就是从Cookie中获取登录时写入的用户信息,经过解密分析重新解析出用户信息到ClaimsPrinciple中,存入到HttpContext里供后续授权判断。

登录:

[HttpPost]
public async Task<IActionResult> Login(string email, string password, bool rememberMe)
{
	var user = await _userManager.FindByEmailAsync(email);
	if (user == null)
	{
		ModelState.AddModelError(string.Empty, "Invalid login");
		return View();
	}
	if (!user.EmailConfirmed)
	{
		ModelState.AddModelError(string.Empty, "Confirm your email first");
		return View();
	}
var passwordSignInResult = await _signInManager.PasswordSignInAsync(user, password, isPersistent: rememberMe, lockoutOnFailure: false);
if (!passwordSignInResult.Succeeded)
{
	ModelState.AddModelError(string.Empty, "Invalid login");
	return View();
}

return Redirect("~/");

}

登出:

[HttpPost]
public async Task<IActionResult> Logout()
{
	await _signInManager.SignOutAsync();
	return Redirect("~/");
}

值得注意的是,这里登出用的是POST操作,因为登出涉及到状态的改变。

 

posted @ 2020-03-31 20:39  mosakashaka  阅读(892)  评论(0编辑  收藏  举报