微软在早期 .NET Framework 时代,针对 ASP.NET 的用户登录身份认证,提供了 Forms 认证实现方案。后来在推出 ASP.NET Core 之后,采用 Claim 认证替代了 Forms 认证,两者本质上都是基于 Cookie 加解密的认证方式,学习和使用起来非常简单,比较适合在小型项目中使用,主要是方便。
假设我们现在已经创建好了一个基于 .NET5 模型-视图-控制器 的 ASP.NET Core Web 应用 (这里就不介绍创建过程了),下面我就基于 .NET5 网站,介绍一下 Claim 认证的具体实现方式:
1 修改 appsettings.json 配置文件,增加有关 Cookie 的相关配置
在实际开发场景中,我们一般将 Cookie 相关信息进行配置,所以我们增加以下配置信息:
{ // 这里采用了两级嵌套的 json 配置方式 "web": { // 用户未登录时,自动跳转的目标路由地址 "loginUrl": "/Home/Login", // Cookie 的名字 "cookieName": "UserAuth", // Cookie 保存的路径,这里保存在根路径 "cookiePath": "/", // Cookie 的所属的域名 "cookieDomain": ".demo.com" } }
其实上面的 json 配置中, Cookie 的保存路径,所属域名,都可以配置为空,这样不影响 Claim 认证的实现。
但是有一种情况例外,那就是万恶的 IE 浏览器。对于 IE 浏览器来说,上面的 json 配置中,必须配置好 Cookie 的保存路径和域名,否则无法生成 Cookie,无法实现 Claim 认证。
如果你实施的项目不考虑 IE 浏览器的话,那就很幸运了。但是目前国内的很多银行、政府、国企,未来很长一段时间仍然还在使用 IE 浏览器,所以有时候还得被迫考虑 IE 浏览器。
2 修改 Startup.cs 文件,增加 Claim 认证支持
需要修改两个地方 ConfigureServices 方法 和 Configure方法
在 ConfigureServices 方法 中添加 Cookie 认证方式,在 Configure方法 中使用认证和授权。
注意:Configure 方法每行代码的顺序位置很重要,新增加的两行代码(app.UseAuthentication 和 app.UseAuthorization 必须在 app.UseRouting 之后,app.UseEndpoints 之前)
public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); //添加 Cookie 认证方式 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { //从配置节中读取配置的 Cookie 信息 options.LoginPath = Config["web:loginUrl"]; options.Cookie.Name = Config["web:cookieName"]; options.Cookie.Path = Config["web:cookiePath"]; options.Cookie.Domain = Config["web:cookieDomain"]; }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseStaticFiles(); app.UseRouting(); //使用认证和授权 app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints =>{ endpoints.MapControllerRoute( name: "default", pattern: "{controller=Web}/{action=Index}"); }); }
3 在相关 Controller 中的 Action 方法添加 Claim 认证的代码
假设我们在登录页面,输入了用户名密码,提交到后台的 Home/Login 中,该 Action 已经在数据库中验证了用户名和密码的正确性,接下来就要生成 Claim 认证的 Cookie,代码如下:
public IActionResult Login(string name, string pwd) { //......此处省略了去数据库验证用户名和密码,以及获取用户的权限的相关代码 //可以添加额外的信息存储在cookie中 //这里把从数据库中获取的【用户角色】和【权限码】进行存储 var claims = new[] { new Claim("Power", power), new Claim("Role", role) }; //这里存储【用户名】 //这样后面就可以通过 User.Identity.Name 获取到用户名了 GenericIdentity gi = new GenericIdentity(name); ClaimsIdentity ci = new ClaimsIdentity(gi, claims, CookieAuthenticationDefaults.AuthenticationScheme, "", ""); ClaimsPrincipal cp = new ClaimsPrincipal(ci); Task.Run(async () => { //生成 Cookie 并保存到硬盘中,指定 Cookie 的过期时间 await HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme,cp, new AuthenticationProperties() { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(60), AllowRefresh = true }); }).Wait(); return Content("login success"); }
前端通过 Ajax 调用后端登录方法后,如果成功,然后跳转到网站主页即可。
4 控制 Controller 和 Action 的访问
我们可以通过在 Controller 上或 Action 上增加 [Authorize] 标记 或 [AllowAnonymous] 标记,来控制用户对具体 Controller 或 Action 的访问,这跟以前的 Forms 认证的控制方式一模一样。
[Authorize] 标记:
表示必须拥有 Claim 的 Cookie 才能访问,否则就会跳转到指定的页面,我们一般指定跳转到登录页面。可以放到 Controller 或 Action 上。如果该标记添加到 Controller 上,则该 Controller 下的所有 Action 都会验证是否拥有 Claim 的 Cookie。
[AllowAnonymous] 标记:
表示允许匿名用户访问,没有登录也可以访问,也就是不需要 Claim 的 Cookie 就可以访问。可以放到 Controller 或 Action 上。通常的使用场景是:在 Controller 上放了 [Authorize] 之后,在该 Controller 下具体的一小部分 Action 上放 [AllowAnonymous] 标记。这样就保证 Controller 下只有一小部分 Action 允许未登录用户访问,其它的 Action 必须用户登录后才能访问。
5 获取 Claim 的 Cookie 中的信息以及退出登录
在上面第 3 步中,我们通过 GenericIdentity gi = new GenericIdentity(name) 这行代码存储登录的用户名,因此我们可以通过 User.Identity.Name 获取到登录的用户名。
下面列出获取 Cookie 中存储的其它信息,比如上面存储的 Power 和 Role
[Authorize] public IActionResult Display() { StringBuilder sb = new StringBuilder(); // 遍历获取所有的存储信息 foreach (var cl in HttpContext.User.Claims) { sb.AppendLine($"{cl.Type}|{cl.Value}"); } // 通过便捷的方式获取登录的用户名 sb.AppendLine(User.Identity.Name); // 想要获取到具体的一项存储信息,比如仅仅想获取权限码和角色 sb.AppendLine(User.FindFirstValue("Power")); sb.AppendLine(User.FindFirstValue("Role")); return Content(sb.ToString()); }
如果想要用户退出登录,销毁 Cookie 的话,可以采用以下代码:
public IActionResult LogOut() { if (User.Identity.IsAuthenticated) { Task.Run(async () => { await HttpContext.SignOutAsync(); }).Wait(); } //销毁 Claim 的 Cookie 后,可以跳转到登录页面 return RedirectToAction("Index", "Web"); }
6 优缺点闲谈
到此为止,ASP.NET Core 网站使用 Claim 认证的方式,实现用户登录,访问时进行身份认证方案,已经介绍完了。如果你想要快速搭建轻量级网站应用的话,使用 Claim 认证方式是一种非常方便快速的方案。
但是不建议在大中型项目中使用,还是采用主流的 token + redis 的方案实现用户登录认证比较好,原因如下: