Cookie 认证是ASP.NET Core用来实现客户自定义认证逻辑,没有使用ASP.NET Core Identity
1 ASP.NET Core Cookie 认证例子
在.NET Core我们通常使用三步来配置cookie认证,第一步是使用AddAuthentication和AddCookie添加Authentication服务,第二步指定app必须使用Authentication&Authorization中间件,最后在需要 cookie 授权的控制器和操作上应用 [Authorize] 属性
2 配置
首先我们需要配置Cookie认证,如下代码展示如何配置Cookie认证
using Microsoft.AspNetCore.Authentication.Cookies;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Home/Login";
options.Cookie.Name = ".AspNetCore.Cookies";
options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
options.SlidingExpiration = true;
});
var app = builder.Build();
在上面代码我们在AddAuthentication()方法中使用CookieAuthenticationDefaults.AuthenticationScheme参数设置应用程序默认认证方法
这意味着登录成功后将为通过身份验证的用户创建一个cookie,这个cookie名字为.ASPNetCore.Cookies
我们设置HomeController的Login方法中登录URL
options.LoginPath = "/Home/Login";
这意味着如果一个未授权的用户尝试访问应用程序安全的URL时将会被自动跳转到/Home/Login, 在登录页面输入用户名和密码进行授权
第二件要做的事情是告诉应用程序用认证和授权,通过添加如下代码实现:
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
调用UseAuthentication& UseAuthorization()方法
3 认证和授权
现在我们在ASP.NET Core 应用程序中使用Cookie认证,在这个应用程序创建2个Controllers如下图所示:
3.1 HomeController.cs
在Home控制器我们有Login和Logout特性,使用Login特性,用户能够登录应用程序,使用Logout特性用户能够退出应用程序
3.2 SecuredController.cs
Secured控制器能只允许登录的用户进行访问,这个控制器有[Authorize]特性
创建一个Controller叫SecuredController.cs,添加[Authorize]特性,所有控制器内部的方法都继承了authorize特性,这意味着所有的action方法只允许授权的用户访问
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace AspNetCore.Cookie.Controllers
{
[Authorize]
public class SecuredController : Controller
{
public IActionResult Index()
{
return View();
}
}
}
接下来,使用下面代码创建Index试图在Views->Secured文件夹
<form method="post" asp-controller="Home" asp-action="Logout">
<button class="btn btn-lg btn-primary btn-block">Logout</button>
</form>
这个视图表单有一个button按钮,当button点击时,Home控制器Logout方法被调用,当前用户会退出系统
4 Cookie登录授权
下面代码在Home控制器中添加一个Login方法:
using AspNetCore.Cookie.Models;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using System.Security.Claims;
namespace AspNetCore.Cookie.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
return View();
}
public IActionResult Login()
{
return View();
}
public async Task<IActionResult> Login(string username, string password, string ReturnUrl)
{
if ((username == "Admin") && (password == "Admin"))
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, username)
};
var claimsIdentity = new ClaimsIdentity(claims, "Login");
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
return Redirect(ReturnUrl == null ? "/Secured" : ReturnUrl);
}
else
return View();
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
Login方法使用了string username, string password, string ReturnUrl在参数中,首先进行检查以确定用户的用户名和密码是否都是管理员用户名和密码,在真实环境中,我们将从数据库中获取用户名和密码与用户输入的用户名和密码进行匹配,在这里为了方便我们使用静态的用户名和密码
if ((username == "Admin") && (password == "Admin"))
{
//…
}
接下来,对用户进行授权,使用用户信息创建一个Cookie,构造一个ClaimsPrincipal序列化用户信息并储到Cookie
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, username)
};
var claimsIdentity = new ClaimsIdentity(claims, "Login");
调用SignInAsync()方法对用户进行登录,它使用2个参数:
1.CookieAuthenticationDefaults.AuthenticationScheme
2.ClaimsPrincipal
SignInAsync()方法如下:
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
用户登录之后,用户跳转return URL
return Redirect(ReturnUrl == null ? "/Secured" : ReturnUrl);
现在,在Home文件夹下创建一个Login视图文件代码如下:
<form class="form-horizontal" action="login" role="form" method="post">
<div class="mb-3 row">
<label class="col-sm-1 control-label">用户名</label>
<div class="col-sm-11">
<input name="username" class="form-control" />
</div>
</div>
<div class="mb-3 row">
<label class="col-sm-1 control-label">密码</label>
<div class="col-sm-11">
<input name="password" type="password" class="form-control" />
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-11 offset-sm-1">
<button type="submit" class="btn btn-primary">登录</button>
</div>
</div>
</form>
测试
运行应用程序尝试访问Secured控制器的Index方法,由于用户没有登录因此将会跳转到Login页面,如下图所示:
现在输入用户名和密码点击登录,登录之后将会跳转到Secured页面,一旦用户经过身份验证,.ASPNetCore.Cookies 的 Cookie 将被创建并存储在浏览器中,我们可以在浏览器的“开发者工具”的“应用程序”区域中看到这个 Cookie,如下图所示
Cookie认证超时
我们可以使用ConfigureApplicationCookie方法设置Cookie的期限,下面代码以滑动方式启用cookie的超时时间:
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Home/Login";
options.Cookie.Name = ".AspNetCore.Cookies";
options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
options.SlidingExpiration = true;
});
Cookie认证返回URL
应用程序会记住用户在身份验证之前在浏览器中打开的安全 URL,因此应用程序将用户导向到登录页面并且添加用户请求的地址,用户尝试打开的url被添加到浏览器查询字符串中,一旦用户成功授权,应用程序从查询字符串中读取return url,并跳转到这个url
当我们在浏览器中打开secured地址,页面会跳转到https://localhost:7262/Home/Login?ReturnUrl=%2FSecured,注意查询字符串值包含Return url, 当我们登录成功之后,应用程序将跳转到这个url
4 Logout特性
接下里,添加Logout方法在Home控制器中,包含下面代码:
[HttpPost]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync();
return RedirectToAction("Index", "Home");
}
调用SignOutAsync()方法退出当前用户,这个方法从浏览器中移除认证Cookie
源代码地址:
https://github.com/bingbing-gui/Asp.Net-Core-Skill/tree/master/Fundamentals/AspNetCore.Security/AspNetCore.Cookie
参考文献
https://www.yogihosting.com/aspnet-core-cookie-authentication/
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-7.0