Identity是Asp.Net Core全新的一个用户管理系统,它是一个完善的全面的庞大的框架,提供的功能有:

  • 创建、查询、更改、删除账户信息

  • 验证和授权

  • 密码重置

  • 双重身份认证

  • 支持扩展登录,如微软、Facebook、google、QQ、微信等

  • 提供了一个丰富的API,并且这些API还可以进行大量的扩展

接下来我们先来看下它的简单使用。首先在我们的DbContext中需要继承自IdentityDbContext。

    public class AppDbContext:IdentityDbContext
{ public AppDbContext(DbContextOptions<AppDbContext> options):base(options)
{
} public DbSet<Student> Students { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Seed();
}
}

然后在Startup中注入其依赖,IdentityUser和IdentityRole是Identity框架自带的两个类,将其绑定到我们定义的AppDbContext中。

services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<AppDbContext>();

最后需要添加中间件UseAuthentication。

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//如果环境是Development,调用 Developer Exception Page
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseStatusCodePages();
app.UseStatusCodePagesWithReExecute("/Error/{0}");
}
app.UseStaticFiles(); app.UseAuthentication(); app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
}

接下来我们就可以使用数据库迁移 Add-Migration 来添加迁移,然后update-database我们的数据库。

可以在数据库中看到其生成的表。

在完成数据迁移之后,我们再来看下Identity中如何完成用户的注册和登录。

我们定义一个ViewModel,然后定义一个AccountController来完成我们的注册和登录功能。Asp.Net Core Identity为我们提供了UserManger来对用户进行增删改等操作,提供了SignInManager的SignInAsync来登录,SignOutAsync来退出,IsSignedIn来判断用户是否已登录等。

public class RegisterViewModel
{
[Required]
[Display(Name = "邮箱地址")]
[EmailAddress]
public string Email { get; set; } [Required]
[Display(Name = "密码")]
[DataType(DataType.Password)]
public string Password { get; set; } [DataType(DataType.Password)]
[Display(Name = "确认密码")]
[Compare("Password",
ErrorMessage = "密码与确认密码不一致,请重新输入.")]
public string ConfirmPassword { get; set; }
}
public class LoginViewModel
{ [Required]
[EmailAddress]
public string Email { get; set; } [Required]
[DataType(DataType.Password)]
public string Password { get; set; } [Display(Name = "记住我")]
public bool RememberMe { get; set; }
}
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using StudentManagement.ViewModels;
using System.Threading.Tasks; namespace StudentManagement.Controllers
{
public class AccountController:Controller
{
private UserManager<IdentityUser> userManager;
private SignInManager<IdentityUser> signInManager; public AccountController(UserManager<IdentityUser> userManager,
SignInManager<IdentityUser> signInManager)
{
this.userManager = userManager;
this.signInManager = signInManager;
} [HttpGet]
public IActionResult Register()
{
return View();
} [HttpPost]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
//将数据从RegisterViewModel复制到IdentityUser
var user = new IdentityUser
{
UserName = model.Email,
Email = model.Email
}; //将用户数据存储在AspNetUsers数据库表中
var result = await userManager.CreateAsync(user, model.Password); //如果成功创建用户,则使用登录服务登录用户信息
//并重定向到home econtroller的索引操作
if (result.Succeeded)
{
await signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction("index", "home");
} //如果有任何错误,将它们添加到ModelState对象中
//将由验证摘要标记助手显示到视图中
foreach (var error in result.Errors)
{
if (error.Code== "PasswordRequiresUpper")
{
error.Description = "密码必须至少有一个大写字母('A'-'Z')。";
} //PasswordRequiresUpper
//Passwords must have at least one uppercase ('A'-'Z').
ModelState.AddModelError(string.Empty, error.Description);
}
}
return View(model);
} [HttpPost]
public async Task<IActionResult> Logout()
{
await signInManager.SignOutAsync();
return RedirectToAction("index", "home");
} [HttpGet]
[AllowAnonymous]
public IActionResult Login()
{
return View();
} [HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var result = await signInManager.PasswordSignInAsync(
model.Email, model.Password, model.RememberMe, false);
if (result.Succeeded)
{
if (!string.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("index", "home");
}
}
ModelState.AddModelError(string.Empty, "登录失败,请重试");
}
return View(model);
}
}
}
@model RegisterViewModel

@{
ViewBag.Title = "用户注册";
} <h1>用户注册</h1>
<div class="row">
<div class="col-md-12">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword"></label>
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">注册</button>
</form>
</div>
</div>
@model LoginViewModel

@{
ViewBag.Title = "用户登录";
} <h1>用户登录</h1> <div class="row">
<div class="col-md-12">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<div class="checkbox">
<label asp-for="RememberMe">
<input asp-for="RememberMe" />
@Html.DisplayNameFor(m => m.RememberMe)
</label>
</div>
</div>
<button type="submit" class="btn btn-primary">登录</button>
</form>
</div>
</div>

在实际工作中,我们需要配置密码的复杂度来增强用户信息的安全性。而Asp.Net Core Identity也默认也提供了一套机制PasswordOptions,可以查看其源码。

https://github.com/aspnet/AspNetCore/blob/master/src/Identity/Extensions.Core/src/PasswordOptions.cs

但是有时候我们需要自定义我们的密码校验模式,这时候可以在Startup中注入

services.Configure<IdentityOptions>(options =>
{
options.Password.RequiredLength = ;
options.Password.RequiredUniqueChars = ;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
});

同时,我们希望我们在注册的时候提示错误信息时使用中文显示,可以定义一个继承IdentityErrorDescriber的类。

using Microsoft.AspNetCore.Identity;

namespace StudentManagement.Middleware
{
public class CustomIdentityErrorDescriber : IdentityErrorDescriber
{
public override IdentityError DefaultError()
{
return new IdentityError { Code = nameof(DefaultError), Description = $"发生了未知的故障。" };
} public override IdentityError ConcurrencyFailure()
{
return new IdentityError { Code = nameof(ConcurrencyFailure), Description = "乐观并发失败,对象已被修改。" };
} public override IdentityError PasswordMismatch()
{
return new IdentityError { Code = nameof(PasswordMismatch), Description = "密码错误" };
} public override IdentityError InvalidToken()
{
return new IdentityError { Code = nameof(InvalidToken), Description = "无效的令牌." };
} public override IdentityError LoginAlreadyAssociated()
{
return new IdentityError { Code = nameof(LoginAlreadyAssociated), Description = "具有此登录的用户已经存在." };
} public override IdentityError InvalidUserName(string userName)
{
return new IdentityError { Code = nameof(InvalidUserName), Description = $"用户名'{userName}'无效,只能包含字母或数字." };
} public override IdentityError InvalidEmail(string email)
{
return new IdentityError { Code = nameof(InvalidEmail), Description = $"Email '{email}' is invalid." };
} public override IdentityError DuplicateUserName(string userName)
{
return new IdentityError { Code = nameof(DuplicateUserName), Description = $"User Name '{userName}' is already taken." };
} public override IdentityError DuplicateEmail(string email)
{
return new IdentityError { Code = nameof(DuplicateEmail), Description = $"Email '{email}' is already taken." };
} public override IdentityError InvalidRoleName(string role)
{
return new IdentityError { Code = nameof(InvalidRoleName), Description = $"Role name '{role}' is invalid." };
} public override IdentityError DuplicateRoleName(string role)
{
return new IdentityError { Code = nameof(DuplicateRoleName), Description = $"Role name '{role}' is already taken." };
} public override IdentityError UserAlreadyHasPassword()
{
return new IdentityError { Code = nameof(UserAlreadyHasPassword), Description = "User already has a password set." };
} public override IdentityError UserLockoutNotEnabled()
{
return new IdentityError { Code = nameof(UserLockoutNotEnabled), Description = "Lockout is not enabled for this user." };
} public override IdentityError UserAlreadyInRole(string role)
{
return new IdentityError { Code = nameof(UserAlreadyInRole), Description = $"User already in role '{role}'." };
} public override IdentityError UserNotInRole(string role)
{
return new IdentityError { Code = nameof(UserNotInRole), Description = $"User is not in role '{role}'." };
} public override IdentityError PasswordTooShort(int length)
{
return new IdentityError { Code = nameof(PasswordTooShort), Description = $"密码必须至少是{length}字符." };
} public override IdentityError PasswordRequiresNonAlphanumeric()
{
return new IdentityError { Code = nameof(PasswordRequiresNonAlphanumeric), Description = "密码必须至少有一个非字母数字字符."
};
} public override IdentityError PasswordRequiresDigit()
{
return new IdentityError { Code = nameof(PasswordRequiresDigit), Description = $"密码必须至少有一个数字('0'-'9')." };
} public override IdentityError PasswordRequiresUniqueChars(int uniqueChars)
{
return new IdentityError { Code = nameof(PasswordRequiresUniqueChars), Description = $"密码必须使用至少不同的{uniqueChars}字符。" };
} public override IdentityError PasswordRequiresLower()
{
return new IdentityError { Code = nameof(PasswordRequiresLower), Description = "密码必须至少有一个小写字母('a'-'z')." };
} public override IdentityError PasswordRequiresUpper()
{
return new IdentityError { Code = nameof(PasswordRequiresUpper), Description = "密码必须至少有一个大写字母('A'-'Z')." };
}
}
}

最后需要在注入Identity的时候添加上这个类

services.AddIdentity<IdentityUser, IdentityRole>()
.AddErrorDescriber<CustomIdentityErrorDescriber>()
.AddEntityFrameworkStores<AppDbContext>();

完成登录后,我们需要对访问资源进行授权,需要在controller或者action上使用Authorize属性来标记,也可以使用AllowAnonymous来允许匿名访问,在项目中使用授权需要引入中间件UseAuthentication

 app.UseAuthentication();

但是如果项目中有很多controller需要添加Authorize属性,我们可以在startup中添加全局的授权,代码如下。

services.AddMvc(config => {
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});

一般在用户登录成功后需要重定向到原始的 URL,这个通过请求参数中带returnUrl来实现,但是如果没有判断是否本地的Url时则会引发开放式重定向漏洞。

解决开放式重定向漏洞的方式也很简单,就是在判断的时候添加Url.IsLocalUrl或者直接return LocalRedirect。

if (Url.IsLocalUrl(returnUrl))
{ }
return LocalRedirect(returnUrl);

Asp.Net Core Identity 完成注册登录的更多相关文章

  1. ASP.NET Core Identity Hands On(2)——注册、登录、Claim

    上一篇文章(ASP.NET Core Identity Hands On(1)--Identity 初次体验)中,我们初识了Identity,并且详细分析了AspNetUsers用户存储表,这篇我们将 ...

  2. ASP.NET Core Identity 实战(2)——注册、登录、Claim

    上一篇文章(ASP.NET Core Identity Hands On(1)--Identity 初次体验)中,我们初识了Identity,并且详细分析了AspNetUsers用户存储表,这篇我们将 ...

  3. IdentityServer(12)- 使用 ASP.NET Core Identity

    IdentityServer具有非常好的扩展性,其中用户及其数据(包括密码)部分你可以使用任何想要的数据库进行持久化. 如果需要一个新的用户数据库,那么ASP.NET Core Identity是你的 ...

  4. ASP.NET Core Identity Hands On(1)——Identity 初次体验

    ASP.NET Core Identity是用于构建ASP.NET Core Web应用程序的成员资格系统,包括成员资格.登录和用户数据存储 这是来自于 ASP.NET Core Identity 仓 ...

  5. IdentityServer4 中文文档 -14- (快速入门)使用 ASP.NET Core Identity

    IdentityServer4 中文文档 -14- (快速入门)使用 ASP.NET Core Identity 原文:http://docs.identityserver.io/en/release ...

  6. ASP.NET Core Identity 实战(4)授权过程

    这篇文章我们将一起来学习 Asp.Net Core 中的(注:这样描述不准确,稍后你会明白)授权过程 前情提要 在之前的文章里,我们有提到认证和授权是两个分开的过程,而且认证过程不属于Identity ...

  7. ASP.NET Core Identity 配置 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core Identity 配置 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core Identity 配置 上一章节我们简单介绍了下 Id ...

  8. ASP.NET Core Identity 框架 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core Identity 框架 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core Identity 框架 前面我们使用了 N 多个章节, ...

  9. 使用WebApi和Asp.Net Core Identity 认证 Blazor WebAssembly(Blazor客户端应用)

    原文:https://chrissainty.com/securing-your-blazor-apps-authentication-with-clientside-blazor-using-web ...

随机推荐

  1. bzoj 1415: [Noi2005]聪聪和可可 期望dp+记忆化搜索

    期望dp水题~ 你发现每一次肯定是贪心走 2 步,(只走一步的话就可能出现环) 然后令 $f[i][j]$ 表示聪在 $i$,可在 $j$,且聪先手两个人碰上面的期望最小次数. 用记忆化搜索转移就行了 ...

  2. 爬虫(九):scrapy框架回顾

    scrapy文档 一:安装scrapy a. pip3 install wheel b. 下载twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#tw ...

  3. webpack4温习总结

    webpack是一个模块打包器,可以根据入口文件,随着依赖关系将所有文件打包成js文件. 首先需要node环境,百度一下自己安装 webpack官网地址:https://www.webpackjs.c ...

  4. avalon里面倒计时功能2

    // 转格式 timeStamp: function (second_time) { var time = parseInt(second_time) + "秒"; ) { ; ) ...

  5. LeetCode 第 150 场周赛

    一.拼写单词(LeetCode-1160) 1.1 题目描述 1.2 解题思路 由于给定的chars,每个字母只能用一次,所以用大小为26的数组charsArray来表示a-z(例如charsArra ...

  6. MongoDB与RoboMongo的安装+python基本操作MongoDB

        MongoDB(来自于英文单词“Humongous”,中文含义为“庞大”)是可以应用于各种规模的企业.各个行业以及各类应用程序的开源数据库.作为一个适用于敏捷开发的数据库,MongoDB的数据 ...

  7. [游戏开发]LÖVE2D(1):引擎介绍

    什么是LÖVE引擎 Love引擎是一个非常棒的框架,你可以用来在Lua制作2D游戏.它是免费的,开源的,适用于Windows,Mac OS X,Linux,Android和iOS. 怎么安装 在官网下 ...

  8. ASP.NET MVC传递Model到视图的多种方式之通用方式的使用

    ASP.NET MVC传递Model到视图的多种方式总结——通用方式的使用 有多种方式可以将数据传递到视图,如下所示: ViewData ViewBag PartialView TempData Vi ...

  9. 用户Ip地址和百度地图api接口获取用户地理位置(经纬度坐标,城市)

    <?php   //获取用户ip(外网ip 服务器上可以获取用户外网Ip 本机ip地址只能获取127.0.0.1) function getip(){     if(!empty($_SERVE ...

  10. 5G && 物联网

    可打电话的 2G.能够上网的 3G.满足移动互联网用户需求的 4G 相比,逐步可以商用的 5G 在多重性能上更胜一筹,如 高数据率: 低延迟: 更节能: 有效地降低通信成本: 具备更高的系统容量: 更 ...