ASP.NET Core 如何用 Cookie 来做身份验证
2019-10-11 19:14 音乐让我说 阅读(533) 评论(0) 编辑 收藏 举报前言
本示例完全是基于 ASP.NET Core 3.0。本文核心是要理解 Claim, ClaimsIdentity, ClaimsPrincipal,读者如果有疑问,可以参考文章 理解ASP.NET Core验证模型(Claim, ClaimsIdentity, ClaimsPrincipal)不得不读的英文博文。
代码
项目文件 csproj 的配置
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> <ItemGroup> </ItemGroup> </Project>
Program.cs
注意: ASP.NET Core 3.0 的配置和 v2.2 稍微有一点不同。
public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); }
Startup
注意:不管是 ConfigureServices 方法,还是 Configure 方法,配置的顺序至关重要,有可能明明配置了 XX,运行时却总是无效。比如笔者实验时,把 app.UseAuthentication(); 写到了 app.UseRouting() 前面,结果导致运行时,标记在 Action 方法上面的 [Authorize] 总是无效,结果发现是注册的顺序搞错了,大家一定要注意这点。
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) // Sets the default scheme to cookies .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { options.AccessDeniedPath = "/account/denied"; options.LoginPath = "/account/login"; }); services.AddControllersWithViews(); // Example of how to customize a particular instance of cookie options and // is able to also use other services. // will override CookieAuthenticationOptions, such as LoginPath => "/account/hello" //services.AddSingleton<IConfigureOptions<CookieAuthenticationOptions>, ConfigureMyCookie>(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseDeveloperExceptionPage(); // Temp Open //app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); // Remember to put it behind app.UseRouting() app.UseAuthorization(); // Remember to put it behind app.UseRouting() app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } }
HomeController
在需要授权才能访问的 Action 方法上标记 [Authorize]
public class HomeController : Controller { private readonly ILogger<HomeController> _logger; public HomeController(ILogger<HomeController> logger) { _logger = logger; } public IActionResult Index() { return View(); } [Authorize] public IActionResult MyClaims() { return View(); } public IActionResult Privacy() { return View(); } }
_Layout.cshtml
在这里面,可以通过 @User.Identity.IsAuthenticated 来判断用户是否已经进行了授权,如果已经授权,则显示 “Logout” 链接。
<li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a> </li> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="MyClaims">My Claims</a> </li> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy_@(User.Identity.IsAuthenticated)</a> </li> @if (User.Identity.IsAuthenticated) { <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Logout">Logout</a> </li> }
AccountController
注意:由于之前我们在 Starup 中配置了 CookieAuthenticationOptions 类(Microsoft.AspNetCore.Authentication.Cookies. CookieAuthenticationOptions)的 options.LoginPath = "/account/login"; 这时候如果访问 /home/MyCliams 时,会自动跳转到 /account/login。
ApplicationUser
public class ApplicationUser { public string Email { get; set; } public string FullName { get; set; } }
LoginViewModel
public class LoginViewModel { public string UserName { get; set; } public string Password { get; set; } }
public class AccountController : Controller { [HttpGet] public IActionResult Login(string returnUrl = null) { ViewData["ReturnUrl"] = returnUrl; return View(); } private async Task<ApplicationUser> AuthenticateUser(string email, string password) { // For demonstration purposes, authenticate a user // with a static email address. Ignore the password. // Assume that checking the database takes 500ms await Task.Delay(500); //if (email == "maria.rodriguez@contoso.com") //{ return new ApplicationUser() { Email = "maria.rodriguez@contoso.com", FullName = "Maria Rodriguez" }; //} //else //{ // return null; //} } [HttpPost] public async Task<IActionResult> Login(LoginViewModel loginViewModel, string returnUrl = null) { if (!ModelState.IsValid) { return Content("validation fail"); } var user = await AuthenticateUser(loginViewModel.UserName, loginViewModel.Password); if (user == null) { ModelState.AddModelError(string.Empty, "Invalid login attempt."); return View(); } ViewData["ReturnUrl"] = returnUrl; var claims = new List<Claim> { new Claim(ClaimTypes.Name, user.Email), new Claim("FullName", user.FullName), new Claim(ClaimTypes.Role, "Administrator"), }; var claimsIdentity = new ClaimsIdentity( claims, CookieAuthenticationDefaults.AuthenticationScheme); var authProperties = new AuthenticationProperties { //AllowRefresh = <bool>, // Refreshing the authentication session should be allowed. //ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10), // The time at which the authentication ticket expires. A // value set here overrides the ExpireTimeSpan option of // CookieAuthenticationOptions set with AddCookie. //IsPersistent = true, // Whether the authentication session is persisted across // multiple requests. When used with cookies, controls // whether the cookie's lifetime is absolute (matching the // lifetime of the authentication ticket) or session-based. //IssuedUtc = <DateTimeOffset>, // The time at which the authentication ticket was issued. //RedirectUri = <string> // The full path or absolute URI to be used as an http // redirect response value. }; await HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authProperties); if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } else { return Redirect("/"); } } public IActionResult AccessDenied(string returnUrl = null) { return View(); } public async Task<IActionResult> Logout() { #region snippet1 await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); #endregion return Redirect("/"); } }
上面的代码即包含“登录”方法,又包含登出方法。
await HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authProperties);
关于 Claim, ClaimsIdentity, ClaimsPrincipal,读者如果有疑问,可以参考文章 理解ASP.NET Core验证模型(Claim, ClaimsIdentity, ClaimsPrincipal)不得不读的英文博文
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
MyClaims.cshtml
@using Microsoft.AspNetCore.Authentication <h2>HttpContext.User.Claims</h2> <dl> @foreach (var claim in User.Claims) { <dt>@claim.Type</dt> <dd>@claim.Value</dd> } </dl> <h2>AuthenticationProperties</h2> <dl> @{ var taskAuth = await Context.AuthenticateAsync(); } @if(taskAuth != null && taskAuth.Properties != null && taskAuth.Properties.Items != null) { @foreach (var prop in taskAuth.Properties.Items) { <dt>@prop.Key</dt> <dd>@prop.Value</dd> } } else { <dt>no data.</dt> } </dl>
运行截图
1. 没有登录的情况下
2. 登录界面
3. 登录成功
谢谢浏览!
作者:音乐让我说(音乐让我说 - 博客园)
出处:http://music.cnblogs.com/
文章版权归本人所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。