十七、安全
在本章中,我们将涵盖以下主题:
- 验证在 ASP.NET 使用 cookie 认证
- 使用授权服务器进行身份验证
- 身份管理
- 用 ASP 保护数据.NET Core
- 哈希
- 加密
介绍
在本节中,我们将看看在 ASP 中身份验证是如何工作的.NET 的核心。
All examples in this chapter can be found at https://github.com/polatengin/B05277/tree/master/Chapter17 GitHub repo.
HTTP 协议是一种无状态的响应请求协议。 这意味着 HTTP 服务器在收到请求后可以生成响应,而且它不会记住以前的请求及其结果。 每个请求都是单独处理的。
例如,如果应用要求您首先登录,开发人员应该处理所需的逻辑流,以便在用户还没有登录时重定向到用户登录页面。
因此,每个请求都应该拥有要成功处理的所有信息(如果用户已登录或未登录,用户是谁,以及他们的权限)。
如果一个糟糕的用户位于客户机和服务器之间,他们可以阅读包,并很容易地假装是其他人。
大多数服务器端框架(ASP.NET、Java、Ruby 等)提供了一些机制,为开发人员提供了从服务器内存存储和访问这类信息的解决方案。
ASP.NET 也不例外,它有 api 来存储/访问/处理服务器内存中的用户信息。
Authentication is the process of determining whether someone or something is, in fact, who or they say they are ,what it says it is: http://searchsecurity.techtarget.com/definition/authentication. Authorization is the process of giving someone permission to do or have something: http://searchsoftwarequality.techtarget.com/definition/authorization.
真实世界中的身份验证
想象一下,你把家里的前门锁上,然后把钥匙藏在门垫下面。 找到并使用钥匙打开前门的人就可以进入整个房子。
简单的锁没有任何机制来确定钥匙持有者是房主还是小偷。
如果你用虹膜扫描仪代替一个简单的锁,你可以同时授权和认证用户。
虹膜扫描仪首先确定用户(身份验证),然后确定允许或拒绝用户访问家庭的权限(授权)。
验证样本
让我们创建一个新的 ASP.NET Core 2.0 web 项目,并查看流程:
An example project can be found at: https://github.com/polatengin/B05277/tree/master/Chapter17/0-AuthenticationSample.
- 首先,打开终端/命令提示符,并导航到我们想要创建项目的文件夹。 你可以在 GitHub(https://github.com/polatengin/B05277)上找到以下项目:
dotnet new web -n AuthenticationSample
dotnet restore
- 现在我们可以在 Visual Studio Code 中打开
AuthenticationSample
文件夹,用下面的代码替换app.Run()
方法体:
var list = new[] {
new { Id=1, Title="Istanbul" },
new { Id=2, Title="New York" },
new { Id=3, Title="Madrid" },
new { Id=4, Title="Rome" },
new { Id=5, Title="Vienna" },
};
var json = JsonConvert.SerializeObject(list);
context.Response.ContentType = "text/json";
await context.Response.WriteAsync(json);
- 我们可以在终端/命令提示符中运行以下命令来编译和运行项目:
dotnet run
- 现在我们可以在浏览器中导航到
http://localhost:5000
,查看列表变量的 JSON 输出:
Up to this point, everything went well. But other users can call this endpoint. What if I want to bar unauthorized users?
验证在 ASP.NET,使用 cookie 身份验证
cookie 认证机制发送一个安全的带有授权信息的 cookie。 客户端向服务器发出的每个请求都包含该安全 cookie,服务器端应用可以从安全 cookie 中识别用户。
An example project can be found at: https://github.com/polatengin/B05277/tree/master/Chapter17/1-CookieAuthenticationSample.
准备
要完成这个示例,您需要创建一个简单的 ASP.NET Core 2.0 项目,并在其中添加一些库。
怎么做……
我们将创建一个新的 ASP.NET Core Web 项目,并添加所需的库来使用 cookie 在其中保存经过身份验证的用户信息:
- 让我们创建一个新的 ASP.NET Core web 项目,并使用 cookie 认证配置它:
dotnet new web -n CookieAuthenticationSample
dotnet add package Microsoft.AspNetCore.Authentication.Cookies
dotnet restore
- 现在我们需要在
Startup.cs
文件中在Configure()
方法的app.UseMvc()
或app.UseMvcWithDefaultRoute()
行之前添加以下行:
app.UseAuthentication();
- 我们还需要在
ConfigureServices()
方法中添加以下一行,在services.AddMvc()
行之前,如下:
services.AddAuthentication("CookieAuthenticationScheme")
.AddCookie("CookieAuthenticationScheme", options => {
options.LoginPath = "/Home/Login";
});
LoginPath
属性是用户尚未登录的重定向点。
- 如果你需要返回一个 401 HTTP 状态码而不是重定向,这可以很容易地通过替换之前的代码与以下:
services.AddAuthentication("CookieAuthenticationScheme")
.AddCookie("CookieAuthenticationScheme", options => {
options.Events.OnRedirectToLogin = (context) => {
context.Response.StatusCode = 401;
return Task.CompletedTask;
};
});
- 我们需要一个数据模型来保存用户凭证; 让我们创建一个文件夹,命名为
Models
,并添加一个LoginModel.cs
文件:
public class LoginModel
{
public string Email { get; set; }
public string Password { get; set; }
}
- 现在我们在
Controllers
文件夹中创建一个HomeController.cs
文件,并向其添加Login()
操作:
[HttpGet]
public IActionResult Login()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Login(LoginModel model)
{
if(LoginUser(model.Email, model.Password))
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Email, model.Email)
};
var identity = new ClaimsIdentity(claims, "login");
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync("CookieAuthenticationScheme", principal);
//Redirect user to home page after login.
return RedirectToAction(nameof(Index));
}
return View();
}
- 让我们添加
Login()
动作,以显示Views
/Home
文件夹中的Login.cshtml
文件,其中包含以下 HTML:
<form method="post">
<input type="email" name="email" />
<input type="password" name="password" />
<input type="submit" value="Login" />
</form>
- 其次,
Login()
方法接收电子邮件和密码字段值,并将它们传递给以下LoginUser()
方法:
private bool LoginUser(string email, string password)
{
//TODO: add DB logic here
return true;
}
The LoginUser()
method is just a placeholder; you should replace it with database backend logic.
- 如果
LoginUser()
方法返回 true,Login()
操作将创建一个新的Claim
并使用该Claim
创建一个新的ClaimIdentity
。
HttpContext.SignInAsync()
方法(来自我们在开始安装的Microsoft.AspNetCore.Authentication.Cookies
NuGet 包)接收一个模式名和ClaimsPrincipal
。
- 我们从前面创建的
ClaimsIdentity
实例化了一个新的ClaimsPrincipal
。
It's important that we should provide the correct schema names, which we stated in the AddAuthentication()
call in the ConfigureServices()
method in the Startup.cs
file.
-
在使用
HttpContext.SignInAsync()
方法之后,我们可以将用户重定向到一个安全页面。HttpContext.SignOutAsync()
方法之前的每个请求都可以用来保护页面。 -
HttpContext.SignOutAsync()
方法使用提供的模式名清除AuthenticationCookie
,然后,对安全页面的请求将失败并重定向到登录页面。 - 现在我们准备将一些页面标记为安全的。 这是一个相对简单的任务; 我们只需要用
[Authorize]
类来注释动作或控制器:
[Authorize]
public class MailboxController : Controller
{
public IActionResult Index()
{
return View();
}
}
它是如何工作的…
在身份验证之前,/mailbox/index
页面会重定向到登录页面。 身份验证之后,/mailbox/index
页面将工作。
基本上,应用拒绝认证前的每个请求,并将它们重定向到登录页面。
身份验证后,浏览器会接收一个身份验证 cookie,并接受需要身份验证的请求。
使用授权服务器进行身份验证
如果有人试图访问你的 web 应用的安全部分,但他们还没有登录,应用会将用户重定向到授权服务器,以识别他们自己。 通常,这意味着用户输入他们的凭证(用户名、电子邮件、密码等等)。
授权服务器只需完成一项工作,即使用用户的凭据对其进行身份验证并返回索赔。
索赔基本上是授予特权列表。 一个用户可以有多个声明来使用应用的某些部分,比如查看账单历史记录、添加账单、从历史记录中删除账单都是不同的声明。
一旦授权服务器用提供的凭证验证了用户,它就生成一个映射到用户的令牌,它要么向用户发出该令牌,要么将用户重定向到应用。
如果用户试图访问 web 应用的安全部分,它将检查令牌,并在令牌有效的情况下授权用户。
一些授权服务器:
- Azure Active Directory:https://docs.microsoft.com/en-us/azure/active-directory/active-directory-whatis
- Auth0:https://auth0.com
- 谷歌 oAuth:https://developers.google.com/identity/protocols/OAuth2ServiceAccount
- Facebook 登录:https://developers.facebook.com/docs/facebook-login
准备
要使用授权服务器,我们需要创建一个项目,添加 NuGet 包,并在授权服务器提供者中创建帐户。
有些授权提供程序需要您创建开发人员帐户。
怎么做……
让我们创建一个新的 ASP.NET Core web 项目和添加 Facebook 认证和谷歌认证 NuGet 包; 为此,运行以下命令:
An example project can be found at: https://github.com/polatengin/B05277/tree/master/Chapter17/2-FacebookGoogleAuthenticationSample.
dotnet new web -n FacebookGoogleAuthenticationSample
dotnet add package Microsoft.AspNetCore.Authentication.Facebook
dotnet add package Microsoft.AspNetCore.Authentication.Google
dotnet restore
使用 Facebook 作为授权服务器
现在我们需要在 Facebook 开发者门户网站(http://developers.facebook.com)注册我们的项目为 Facebook 应用:
- 首先,我们应该在 Facebook 开发者门户中输入 Facebook 凭据。 使用 Add a new app 按钮创建一个应用,并填写以下表格:
- 在创建一个应用之后,我们应该看到仪表板。 在左侧,有一个+添加产品菜单; 当点击它时,它将显示推荐产品页面。
-
Facebook 登录面板就是我们要找的。 它负责为特定的 Facebook 应用添加一个功能,以便使用 Facebook 凭据登录。
-
我们只需要点击 Setup 按钮并调整一些设置; 就是这么简单:
- 在打开的页面中,只需填写 Valid OAuth 并使用
http://localhost:5000/signin-facebook
URL 重定向 uri 文本框。 这个 URL 将是我们项目的端点,它处理 Facebook Login 的返回值。
We should change this URL to our production endpoint before publishing our app in a production environment.
我们还没有改变其他复选框或文本框。 但是,在将项目发布到生产环境之前,我们应该重新访问前面的页面。
- 保存 Facebook 登录页面设置后,我们应该回到 Facebook 应用的 Dashboard 页面,找到应用 ID 和应用秘密信息:
- 点击 Show 按钮查看 App Secret。
- 现在回到项目。 让我们打开
Startup.cs
文件,在ConfigureServices()
方法中添加一些代码:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=FacebookGoogleAuthenticationSample;Data Source=."));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(
CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(o => o.LoginPath = new PathString("/home/login"))
.AddFacebook(facebook =>
{
facebook.AppId = "208906605857885";
facebook.AppSecret = "90e620f09fbadfaa01f74e2bc3";
});
services.AddMvc();
}
- 你应该将 Facebook 应用 ID 和 Facebook 应用秘密更改为你自己的应用 ID,并将 App 秘密更改为你自己的。
下面是之前提供的代码块中的几个重要指针:
Identity is enabled for the application by calling UseAuthentication
in the Configure
method and AddIdentity
and AddAuthentication
in the ConfigureServices
method.
- 在同一个文件(
Startup.cs
)中,我们还应该更改Configure()
方法:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc((routes) =>
{
routes.MapRoute("SigninFacebook", "signin-facebook", new { controller = "Home", action = "FacebookOK" });
routes.MapRoute("Default", "{controller=Home}/{action=Index}");
});
}
现在我们有了一个强大的基线,我们可以继续以下内容:
- 让我们创建
Models
,Views
,和Controllers
文件夹,并在Views
文件夹中创建一个Home
文件夹: - 我们需要在
Models
文件夹中创建一个ApplicationUser.cs
文件:
using Microsoft.AspNetCore.Identity;
public class ApplicationUser : IdentityUser
{
}
- 我们没有添加自定义字段来存储在数据库中。 在
Models
文件夹中也创建ApplicationDbContext.cs
文件:
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
}
- 让我们在
Controllers
文件夹中创建一个HomeController.cs
文件,并为其添加一个构造函数:
private readonly SignInManager<ApplicationUser> _signInManager;
public HomeController(SignInManager<ApplicationUser> signInManager) => this._signInManager = signInManager;
- 使用
_signinManager
变量,我们可以让用户从 Facebook 登录和退出。 首先,从Index()
操作返回索引视图:
public IActionResult Index() => View();
- 我们需要在
Views
/Home
文件夹中有一个Index.cshtml
文件,这是一个相对简单的任务:
<a href="/home/facebook">Facebook Login</a>
- 在
HomeController.cs
文件中,添加Facebook()
动作,如下所示:
public IActionResult Facebook()
{
var properties =
_signInManager.ConfigureExternalAuthenticationProperties
("Facebook", "/Home/FacebookOK");
return Challenge(properties, "Facebook");
}
- 我们使用
_signInManager
变量来处理 FacebookAuthentication
进程。 在 Facebook 登录后,流将继续与FacebookOK()
动作:
public async Task<IActionResult> FacebookOK()
{
var info = await
_signInManager.GetExternalLoginInfoAsync();
//info.Principal
//structure that holds Claims, provided by Facebook
//it includes, Facebook Unique Identifier, Facebook
Email, Facebook Name, etc.
//info.ProviderKey
//Facebook Unique Identifier for logged in user
return RedirectToAction(nameof(Index));
}
-
我们使用
_signInManager
变量获取额外的信息,这些信息将由 FacebookAuthentication
流程提供,然后将用户重定向到Index()
动作/页面。 -
在此重定向之后,用户可以自由地在应用中导航,特别是带有
[Authorize]
属性的动作/控制器,以前无法导航:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[Authorize]
public class MailboxController : Controller
{
public IActionResult Index() => View();
}
现在,我们有向匿名用户开放的页面,以及由 Facebook Login 机制保护的页面,该机制只对登录的用户开放。
使用谷歌作为授权服务器
现在是谷歌认证时间:
- 让我们导航到谷歌开发人员控制台(https://console.developers.google.com),并使用谷歌凭证登录:
- 登录到谷歌开发人员控制台后,我们需要通过单击 create 按钮并填写以下表单来创建一个项目:
- 当我们在谷歌开发人员控制台中创建项目时,它会立即将我们重定向到项目仪表板。
- 我们应该点击启用 API 和服务按钮,然后从列表中选择谷歌+ API:
- 使用谷歌+ API,我们可以为我们的项目启用谷歌身份验证。 只需单击启用按钮。
- 当谷歌为我们的项目启用谷歌+ API 时,它将导航到谷歌+ API 仪表板。
- 我们应该点击 Create Credentials 按钮并填写以下表单:
- 当我们点击“What credentials do I need?” 按钮,谷歌开发人员控制台询问以下问题:
We should change this URL to our production endpoint before publishing our app in a production environment.
表格上有三个重要的信息:
- 现在我们可以点击 Create client ID 按钮; 之后,我们可以复制客户端 ID 并下载客户端证书文件:
客户端凭据文件为 JSON 格式文件(client_id.json
),包含client_id
、project_id
和client_secret
信息。 我们需要这些信息
.AddGoogle(google =>
{
google.ClientId = "1074523660996-lmhrgsuhau31ptca5ajln1334t94b4tj.apps.googleusercontent.com";
google.ClientSecret = "fW3fnRzs7DQa9WEUVbaMhCAL";
})
It is important to change Google Client Id and Google Client Secret values before publishing to production values.
- 我们需要将以下一行添加到
Configure()
方法中,就在SigninFacebook
行之前:
routes.MapRoute("SigninGoogle", "signin-google", new { controller = "Home", action = "GoogleOK" });
- 打开
Views
/Home
文件夹下的Index.cshtml
,修改内容如下:
<a href="/home/facebook">Facebook Login</a>
<br />
<a href="/home/google">Google Login</a>
通过这样做,用户可以登录 Facebook 或谷歌。
- 现在打开
HomeController.cs
文件,添加Google()
动作:
public IActionResult Google()
{
var properties = _signInManager.ConfigureExternalAuthenticationProperties("Google", "/Home/GoogleOK");
return Challenge(properties, "Google");
}
- 我们只需要
HomeController.cs
文件中的GoogleOK()
方法,如下所示:
public async Task<IActionResult> GoogleOK()
{
var info = await
_signInManager.GetExternalLoginInfoAsync();
//info.Principal
//structure that holds Claims, provided by Google
//it includes, Google Unique Identifier, Google Email,
Google Name, etc.
//info.ProviderKey
//Google Unique Identifier for logged in user
return RedirectToAction(nameof(Index));
}
它是如何工作的…
未经授权的请求将以RedirectToAction()
方法响应,并将其重定向到 Facebook 或谷歌的登录方法。
当用户向 Facebook 或谷歌授权服务器(登录页面)中的应用授予权限时,它会向我们的应用携带一个令牌,我们坚持它标识一个个人。
身份管理
现在,我们准备运行我们的应用,并让用户通过 Facebook 登录或谷歌登录机制登录。
Facebook 和谷歌都返回ProviderKey
属性来识别单个用户。 我们可以在数据库中保存该信息,并将其与另一个表(如 ShoppingCart、PurchaseHistory 等)关联起来。 这样,我们可以让 Facebook 和谷歌管理用户,但我们仍然可以在数据库系统中引用该用户。
ASP 的美.NET Core 身份是我们可以轻松地将另一个系统插入其中(如 Linkedin、Microsoft Live、GitHub、Auth0 等)。 它只是在授权服务器系统中添加一个库并创建一个应用。 在 ASP.NET Core,我们可以创建一个空项目,并将其转化为一个具有身份识别功能的完整应用。
微软发布了Microsoft.AspNetCore.Identity
NuGet 包(https://www.nuget.org/packages/Microsoft.AspNetCore.Identity/),帮助开发者轻松创建认证和授权机制。
这个包也是Microsoft.AspNetCore.All
NuGet 包(https://www.nuget.org/packages/Microsoft.AspNetCore.All/)的一部分。
新的 ASP.NET Core web 项目已经包含了Microsoft.AspNetCore.All
NuGet 包,我们不需要在我们的项目中确认身份。
An example project can be found at https://github.com/polatengin/B05277/tree/master/Chapter17/3-AspNetCoreIdentitySample.
准备
在 ASP 中管理身份没有什么特别需要准备的.NET 的核心。 只需创建一个空项目和一些 NuGet 包; 就是这样。
It's assumed that the app can reach a SQL Server, with the required permissions.
怎么做……
让我们深入研究代码:
- 首先,在命令提示符/终端中运行以下脚本创建一个新项目:
dotnet new web -n AspNetCoreIdentitySample
dotnet restore
- 打开
Startup.cs
文件,修改ConfigureServices()
方法,如下:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=;Data Source=."));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc();
}
- 我们设置连接字符串,并使用数据库名称
AspNetCoreIdentitySample
将其指向本地数据库服务器。
It's not a problem if we have no database with that name, EntityFrameworkCore
will create it before using it the first time.
- 此外,我们声明,我们想要在
ApplicationUser
类和ApplicationDbContext
数据库连接层中使用 Identity。 - 现在我们需要更改
Configure()
方法,如下:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvcWithDefaultRoute();
}
- 我们创建一个
Models
文件夹,并添加ApplicationUser.cs
文件,如下所示:
using System;
using Microsoft.AspNetCore.Identity;
public class ApplicationUser : IdentityUser
{
public string CityName { get; set; }
public DateTime JobBeginDate { get; set; }
}
- 我们刚刚创建了一个新文件来开发
ApplicationUser
类。 我们应该注意到,ApplicationUser
类应该继承自IdentityUser
类,而IdentityUser
类来自Microsoft.AspNetCore.Identity
名称空间。IdentityUser
类具有以下属性:
public virtual DateTimeOffset? LockoutEnd { get; set; }
public virtual bool TwoFactorEnabled { get; set; }
public virtual bool PhoneNumberConfirmed { get; set; }
public virtual string ConcurrencyStamp { get; set; }
public virtual string SecurityStamp { get; set; }
public virtual string PasswordHash { get; set; }
public virtual bool EmailConfirmed { get; set; }
public virtual string NormalizedEmail { get; set; }
public virtual string Email { get; set; }
public virtual string NormalizedUserName { get; set; }
public virtual string UserName { get; set; }
public virtual TKey Id { get; set; }
public virtual bool LockoutEnabled { get; set; }
public virtual int AccessFailedCount { get; set; }
- 如果我们需要关于用户的额外信息,我们可以添加一个属性派生类(在本例中是
ApplicationUser
类)。 - 我们添加了
CityName
和JobBeginDate
属性。 - 现在我们需要在
Models
文件夹中创建一个ApplicationDbContext.cs
文件:
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
}
EntityFrameworkCore
自动在目标服务器中创建一个数据库,该数据库中的表,等等。
- 在
Controllers
文件夹中创建HomeController.cs
文件,添加两个类级变量和构造函数,如下所示:
private readonly UserManager<ApplicationUser> userManager;
private readonly SignInManager<ApplicationUser> loginManager;
public HomeController(ApplicationDbContext dc, UserManager<ApplicationUser> userManager, <ApplicationUser> loginManager)
{
this.userManager = userManager;
this.loginManager = loginManager;
dc.Database.EnsureCreated();
}
UserManager
和SignInManager
类管理用户的创建、登录和退出系统。 让我们创建一些动作:
public IActionResult Index() => View();
[HttpGet]
public IActionResult Register() => View();
[HttpGet]
public IActionResult Login() => View();
- 现在我们需要一个包含
Index.cshtml
、Register.cshtml
和Login.cshtml
文件的Views
/Home
文件夹:
<a href="/home/login">Login</a>
<a href="/home/register">Register</a>
<a href="/home/logout">Logout</a>
<h1>Register</h1>
<form action="/home/register" method="post">
<table>
<tr>
<td>UserName</td>
<td><input type="text" name="username" /></td>
</tr>
<tr>
<td>Password</td>
<td><input type="password" name="password" /></td>
</tr>
<tr>
<td>Confirm Password</td>
<td><input type="password" name="confirmPassword" /></td>
</tr>
<tr>
<td>Email</td>
<td><input type="email" name="email" /></td>
</tr>
<tr>
<td>Phone Number</td>
<td><input type="tel" name="phoneNumber" /></td>
</tr>
<tr>
<td>City Name</td>
<td><input type="text" name="cityName" /></td>
</tr>
<tr>
<td>Job Begin Date</td>
<td><input type="date" name="jobBeginDate" /></td>
</tr>
<tr>
<td><input type="submit" value="Register" /></td>
</tr>
</table>
</form>
我们从客户端获得了足够的数据来创建用户。
Notice, we're getting Email
, PhoneNumber
, CityName
, JobBeginDate
<h1>Login</h1>
<form action="/home/login" method="post">
<table>
<tr>
<td>UserName</td>
<td><input type="text" name="username" /></td>
</tr>
<tr>
<td>Password</td>
<td><input type="password" name="password" /></td>
</tr>
<tr>
<td>Remember Me</td>
<td><input type="checkbox" name="rememberme" /></td>
</tr>
<tr>
<td><input type="submit" value="Login" /></td>
</tr>
</table>
</form>
- 让我们回到
HomeController.cs
,添加一个Login()
动作,如下所示:
[HttpPost]
public async Task<IActionResult> Login(string username, string password, string rememberme)
{
var result = await
this.loginManager.PasswordSignInAsync(username,
password, rememberme == "on", false);
return RedirectToAction(nameof(Index));
}
我们使用loginManager
变量通过调用PasswordSignInAsync()
方法来登录用户。 如果我们将false
作为第三个参数传递,那么用户每次打开新浏览器时都必须登录。 如果我们传递true
作为第三个参数,ASP.NET Core Identity 生成一个 cookie,将其传递给客户端,并在他们打开新浏览器时记住它们:
- 注销是一项相对容易的任务; 使用以下代码:
public IActionResult Logout()
{
this.loginManager.SignOutAsync();
return RedirectToAction(nameof(Index));
}
我们只是使用了loginManager
变量并调用了SignOutAsync()
方法来成功地退出应用。
The PasswordSignInAsync()
method creates a cookie if the third parameter is true. The SignOutAsync()
method deletes it, if it exists.
- 最后一个方法是
Register
方法:
[HttpPost]
public async Task<IActionResult> Register(string username, string email, string password, string phoneNumber, string cityName, DateTime jobBeginDate)
{
var user = new ApplicationUser()
{
UserName = username,
Email = email,
PhoneNumber = phoneNumber,
CityName = cityName,
JobBeginDate = jobBeginDate
};
var result = await this.userManager.CreateAsync(user, password);
//TODO: check for
//result.Succeeded and result.Errors
return RedirectToAction(nameof(Index));
}
Register()
方法使用userManager
变量创建用户。 如果我们提供带有第二个参数的密码,则创建的用户将拥有该密码。 否则,创建的用户没有密码。
在前面的代码示例中,我们有结果变量。 Result
变量要么填充Succeeded
属性,要么填充Errors
属性。 我们应该迭代Errors
属性以列出错误,例如密码太短、复杂性不够、没有提供用户名或电子邮件,等等。
如您所见,数据库和所有必需的表都在第一次自动创建。
- 我们可以点击注册链接,填写表格,如下:
数据库中的AspNetUsers
表将有一个新行:
- 现在我们可以登录并使用整个应用:
它是如何工作的…
将应用部署到服务器并运行它。 用户可以创建一个帐户并使用它登录。 所有数据都持久化在 SQL Server 中。
ASP.NET Core Identity 使得使用 SQL Server 数据库向我们的 web 应用添加身份验证和授权机制变得非常容易。
ASP.NET Core Identity 库可以使用 NuGet 包管理器添加到项目中。
基本上,ASP.NET Core Identity 库添加了不同的类来管理应用的用户及其权限。 它还包括一种机制,用于在数据库中生成表来保存用户和相关信息。
用哈希法保护数据
.NET Core 现在在以下操作系统中使用加密 API:
- macOS 上的苹果安全框架
- OpenSSL Linux 上
- 密码 API:下一代(CNG
有两种保护数据的机制:散列和加密。
哈希是一种单向机制,无法将经过哈希处理的数据返回到原始状态。
另一方面,加密是一种双向机制,您可以通过解密将加密的数据返回到原始状态。
有很多算法你可以使用 ASP.NET Core,如 Hash、SHA256、SHA512、AES、RSA、MD5 等。 更多信息,请访问:https://www.nuget.org/packages/System.Security.Cryptography.Algorithms/。
An example project can be found at: https://github.com/polatengin/B05277/tree/master/Chapter17/4-HashingData.
准备
在 ASP 中没有什么特别的需要准备的.NET 的核心。 只需创建一个空项目和一些 NuGet 包; 就是这样。
怎么做……
在这个例子中,我们将使用 SHA512:
- 让我们新建一个项目,如下所示:
dotnet new console -n HashingData
dotnet restore
- 打开
Program.cs
,修改如下:
static void Main(string[] args)
{
var hashed = CalculateHash("Hello World!");
Console.WriteLine(hashed);
Console.ReadKey();
}
static string CalculateHash(string input)
{
using (var algorithm = SHA512.Create())
{
var b = algorithm.ComputeHash(Encoding.UTF8.GetBytes(input));
return BitConverter.ToString(b).Replace("-", "").ToLower();
}
}
- 我们可以运行这个应用并看到散列输出:
There is no way to unhash or restore original data from a hashed version.
它是如何工作的…
我们可以使用CalculateHash()
方法获取所提供文本的散列值。 这是一个单向的过程,所以我们无法在散列之后获得提供的文本。
ASP.NET Core 有内置的类来为不同的算法散列给定的数据。 散列是一种单向操作,给定的数据在被散列之后不能被检索。
使用加密技术保护数据
ASP 中也有加密算法.NET Core,如 AES、Des、3Des (TripleDes)、Rijndael、RC2 等。 更多信息,请访问https://docs.microsoft.com/en-us/dotnet/standard/security/cryptography-model。
An example project can be found at https://github.com/polatengin/B05277/tree/master/Chapter17/5-EncryptDecryptData.
准备
没有什么特别的准备在 ASP 加密.NET 的核心。 只需创建一个空项目和一些 NuGet 包; 就是这样。
怎么做……
- 让我们创建一个新项目,深入研究代码,以便更好地理解 ASP 中的加密和解密.NET Core:
dotnet new console -n EncryptDecryptData
dotnet restore
- 现在打开
Program.cs
文件,添加Encrypt()
和Decrypt()
方法,如下所示:
static string Encrypt(string text, string key)
{
var _key = Encoding.UTF8.GetBytes(key);
using (var aes = AES.Create())
{
using (var encryptor = aes.CreateEncryptor(_key, aes.IV))
{
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (var sw = new StreamWriter(cs))
{
sw.Write(text);
}
}
var iv = aes.IV;
var encrypted = ms.ToArray();
var result = new byte[iv.Length + encrypted.Length];
Buffer.BlockCopy(iv, 0, result, 0, iv.Length);
Buffer.BlockCopy(encrypted, 0, result, iv.Length, encrypted.Length);
return Convert.ToBase64String(result);
}
}
}
}
static string Decrypt(string encrypted, string key)
{
var b = Convert.FromBase64String(encrypted);
var iv = new byte[16];
var cipher = new byte[16];
Buffer.BlockCopy(b, 0, iv, 0, iv.Length);
Buffer.BlockCopy(b, iv.Length, cipher, 0, iv.Length);
var _key = Encoding.UTF8.GetBytes(key);
using (var aes = AES.Create())
{
using (var decryptor = aes.CreateDecryptor(_key, iv))
{
var result = string.Empty;
using (var ms = new MemoryStream(cipher))
{
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (var sr = new StreamReader(cs))
{
result = sr.ReadToEnd();
}
}
}
return result;
}
}
}
在本例中,我们使用 AES 加密器。 Ecnrypt()
和Decrypt()
方法通过方法参数获得加密密钥,对提供的字符串进行加密或解密,并返回加密/解密版本。
- 我们只需要更改
Main()
方法,如下所示:
static void Main(string[] args)
{
var key = Guid.NewGuid().ToString("N");
var original = "Hello World!";
var encrypted = Encrypt(original, key);
var decrypted = Decrypt(encrypted, key);
Console.WriteLine(original);
Console.WriteLine(encrypted);
Console.WriteLine(decrypted);
Console.ReadKey();
}
- 当我们运行应用时,我们可以看到原始的、加密的和解密的值如下:
它是如何工作的…
我们可以使用Encrypt()
方法获取所提供文本的加密值。 这是一个双向过程,因此我们可以得到加密文本的解密值。
ASP.NET Core 也有内置的类,可以用不同的算法加密和解密给定的数据。 加密是一种双向操作,所提供的数据经过与加密算法相同的解密操作加密后,即可进行计算。
版权属于:月萌API www.moonapi.com,转载请注明出处