• .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操作,因为登出涉及到状态的改变。

  • 相关阅读:
    Visual C# 2008+SQL Server 2005 数据库与网络开发14.2.3 WWF的功能
    Visual C# 2008+SQL Server 2005 数据库与网络开发14.1.3 WPF的架构
    Visual C# 2008+SQL Server 2005 数据库与网络开发14.3.2 WCF的架构
    Visual C# 2008+SQL Server 2005 数据库与网络开发14.3.1 WCF介绍
    Visual C# 2008+SQL Server 2005 数据库与网络开发14.2.2 WWF的结构
    Visual C# 2008+SQL Server 2005 数据库与网络开发 14.2 Windows Workflow Foundation(WWF)
    Visual C# 2008+SQL Server 2005 数据库与网络开发14.1.4 WPF和XAML
    Visual C# 2008+SQL Server 2005 数据库与网络开发 14.3 Windows Communication Foundation(WCF)
    Visual C# 2008+SQL Server 2005 数据库与网络开发14.2.1 WWF简介
    Visual C# 2008+SQL Server 2005 数据库与网络开发 14.4 小结
  • 原文地址:https://www.cnblogs.com/mosakashaka/p/12608131.html
Copyright © 2020-2023  润新知