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. 怎样运行jar包中的文件

    1. 2.编辑sysmodule.cmd文件 java -cp sysmodule.jar;classes12.jar;mysql-connector-java-5.0.3-bin.jar;jbcl. ...

  2. php cookie 操作

    创建 cookie <?php setcookie(); ?> 取回 Cookie 的值 <?php // Print a cookie echo $_COOKIE["us ...

  3. CodeForces 787 题解

    A题,因为数据范围很小,所以只要暴力即可,如果能相遇一定范围不大,如果范围很大还没相遇一定是不会相遇的了.正解应当是用扩展欧几里得计算这个方程的整数解,再想办法看看有没有正整数解才是. B题,只要看懂 ...

  4. python3实例

    1.一行代码求一个数的阶乘 例如:求5的阶乘 from functools import reduce print((lambda k: reduce(, k+), ))()) 借鉴:https:// ...

  5. 实现Callable接口实现多线程

    package com.roocon.thread.t2; import java.util.concurrent.Callable; import java.util.concurrent.Exec ...

  6. Vue中的动画效果

    Vue 在插入.更新或者移除 DOM 时,提供多种不同方式的应用过渡效果.包括以下工具: 在 CSS 过渡和动画中自动应用 class 可以配合使用第三方 CSS 动画库,如 Animate.css ...

  7. Alpha项目冲刺! Day5-产出

    各个成员今日完成的任务 林恩:任务分工,博客撰写,了解安卓环境搭建 杨长元:安卓本地数据库基本建立 李震:改了图标和背景 胡彤:完善服务端 寇永明:研究测试代码 王浩:研究测试代码 李杰:研究测试代码 ...

  8. firewall-cmd命令详解

    https://blog.csdn.net/GMingZhou/article/details/78090963 实例 # 安装firewalld yum install firewalld fire ...

  9. xgboost 源码学习

    官方代码结构解析,README.MD XGboost 回归时,损失函数式平方误差损失 分类时,是对数自燃损失: Coding Guide ====== This file is intended to ...

  10. linux设置su和sudo为不需要密码

    一 设置sudo为不需要密码   有时候我们只需要执行一条root权限的命令也要su到root,是不是有些不方便?这时可以用sudo代替.默认新建的用户不在sudo组,需要编辑/etc/sudoers ...