Microsoft.AspNetCore.Authentication.Cookies從入門到精通 (一)
Microsoft.AspNetCore.Authentication.Cookies是一個存儲組件,它的主要作用是通過Cookie機制將授權認證的回話信息保存到客戶端中,這與我之前的文章AspNetCore中基於session的身份認證中的HttpSession原理相同,只不過在AspNetCore中基於session的身份認證這篇文章中我們是自己做的一套驗證方案,而篇文章使用的是Asp.Net Core提供的認證方案而已。
開始一個Demo
1.創建一個Razor Page項目:dotnet new razor -n CookieSample
2.打開Startup.cs文件,添加認證代碼
public void ConfigureServices(IServiceCollection services)
{
.......
services.AddAuthentication()
.AddCookie();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
......
app.UseAuthentication();
......
}
只需要幾行代碼我們就在項目中添加了授權認證功能,下面我們看如何在頁面中使用授權認證
3.打開Index.cshtml.cs文件,添加我們的認證代碼
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
public class IndexModel : PageModel
{
public void OnGet()
}
只需要一行代碼,我的頁面就成了一個需要授權認證后才能訪問的頁面,運行項目訪問該頁面查看效果。
4.添加Login頁面,並在該頁面實現登陸認證
當你打開Index頁面的時候你會發現被重定向了到了/Account/Login,此時我們的項目中還沒有這個頁面,現在我們創建Login頁面並添加一些登陸代碼,這里所謂登陸並不是真正的從數據庫里面驗證用戶信息,當然從數據驗證的過程應該在該操作之前,這里的登陸是將我們希望保存在Cookie中的信息傳遞給Asp.Net Core認證組件,並由該組件將信息序列化到Cookie中,由此可見Microsoft.AspNetCore.Authentication.Cookies只是為了存儲
public class LoginModel : PageModel
{
public void OnPost()
{
//這里可能需要去數據驗證一些信息
......
//數據庫驗證通過后,我們需要將部分信息保存到Cookie中
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name,"Tst")
};
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
}
}
這就是我們Demo的全部代碼,是不是很簡單。
使我們的Demo更接近真實的項目
案例需求
通常我們的網站會區分前台與后台,普通用戶可以訪問的我們叫前台,管理員可以訪問的我們叫后台。
1. 我們將基於之前的Demo進行改造,現在我們的項目分為兩類用戶而且每類用戶訪問的頁面還不相同。
2. 我們來創建管理員可以訪問的頁面:/Admin/Index,現在我們有了兩個頁面分別供兩類用戶訪問,但是我們只有一個Login頁面那么怎么才能讓兩類用戶公用也一個登陸頁面呢?
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
......
services.AddAuthentication()
.AddCookie()
.AddCookie("Admin","Admin",options=>{});
......
}
上面代碼我添加一個了一個Admin認證方案用於管理員訪問並在/Admin/Index頁面中顯式設置認證方案:
/Admin/Index
[Authorize(AuthenticationSchemes = "Admin")]
public class IndexModel : PageModel{}
3. 現在我們來修改Login頁面代碼,我們需要根據認證方案來做不同的認證登錄,目的是為了生成認證的cookie信息
public class LoginModel : PageModel
{
//保存認證方案以及友好顯示名稱
[BindProperty]
public IDictionary<string, string> Schemes { get; set; } = new Dictionary<string, string>();
//認證后我們要跳轉到的頁面
[BindProperty]
public string RedirectToUrl { get; set; }
IAuthenticationSchemeProvider _authenticationSchemeProvider;
//構造函數注入方式獲取IAuthenticationSchemeprovider實例
public LoginModel(IAuthenticationSchemeProvider authenticationSchemeProvider)
{
//也可以通過從服務中查找的方式獲取IAuthenticationSchemeprovider實例
//_authenticationSchemeProvider = (IAuthenticationSchemeProvider)HttpContext.RequestServices.GetService(typeof(IAuthenticationSchemeProvider));
_authenticationSchemeProvider = authenticationSchemeProvider;
}
public async Task OnGetAsync(string ReturnUrl)
{
RedirectToUrl = ReturnUrl;
var authenticationSchemes = await _authenticationSchemeProvider.GetAllSchemesAsync();
foreach (var item in authenticationSchemes)
{
//如果我們沒有設置DisplayName,就是用認證方案名稱替換
Schemes.Add(item.Name, item.DisplayName??item.Name);
}
}
public IActionResult OnPost(string scheme,string redirectUrl)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name,scheme)
};
//生成不同認證方案的身份信息
var claimsIdentity = new ClaimsIdentity(claims, scheme);
//將認證信息保存到cookie中
HttpContext.SignInAsync(scheme, new ClaimsPrincipal(claimsIdentity));
return LocalRedirect(redirectUrl);
}
}
我們來回顧一下代碼做了什么事:
首先,我們在構造函數中注入了IAuthenticationSchemeProvider的實例,該實例主要用來獲取認所有的證方案。
然后,我們將選擇的認證方案以及認證后要跳轉的地址Post到Login頁面(這里也需要你提交用戶名密碼等從數據驗證),驗證后並將一些信息寫入Cookie中。
插播一點個人的想法:有人可能會想我們分兩個項目不行嗎?我們搞成微服務不行嗎?我的回答是不行:因為這里的一切教程都是圍繞Microsoft.AspNetCore.Authentication.Cookies的功能進行講解,實現認證的方式有很多種,每個人都會有自己的想法和實現,但我們只講Asp.Net Core提供的東西,這樣有利於統一,也有利於減少學習成本,學習成本少了才有時間去創新。
需求更新:為認證添加過期時間
要求普通用戶認證信息在最后一次訪問的10分鍾后過期,而管理員需要在最后一次訪問的1天后過期。
public void ConfigureServices(IServiceCollection services)
{
......
services.AddAuthentication()
.AddCookie(options=>{
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
})
.AddCookie("Admin","Admin",options=>{
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromDays(1);
});
......
}
我們通過兩個屬性實現了授權過期
SlidingExpiration:通過設置這個參數為Ture可以將過期時間變成滾動(滑動)過期,也就是按最后一次訪問重新計算過期時間,只有設置了滑動Cookie我們的認證信息才更像一個回話狀態。
ExpireTimeSpan:設置過期時間
需求更新:要求使用持久Cookie
在上面的需求中,每當我們關閉瀏覽器,Cookie的狀態就丟失了,這不符合我們的要求,現在我們來修改讓Cookie持久的保存在客戶端。在之前的文章理解cookies中我們介紹了如何讓cookie持久化,原理就是顯式設置Expires和MaxAge。
public void ConfigureServices(IServiceCollection services)
{
......
services.AddAuthentication()
.AddCookie(options=>{
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
//這里我們將Cookie過期時間設置成了365天,你也可以通過MaxAge屬性來設置,不過這里有點需要注意的是,MaxAge會替代Expiration的值
options.Cookie.Expiration = TimeSpan.FromDays(365);
//options.Cookie.MaxAge = TimeSpan.FromDays(365);
})
.AddCookie("Admin","Admin",options=>{
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromDays(1);
options.Cookie.Expiration = TimeSpan.FromDays(365);
});
......
}
只需要一行代碼我們就讓Cookie變成了持久化Cookie,但有個問題,我們將普通用戶的認證Cookie設置為10分鍾過期,但卻將Cookie設置為365天(這里的認證Cookie與Cookie是一個東西,認證Cookie的過期是認證服務驗證的,而Cookie本身是瀏覽器驗證的,如果瀏覽器認為Cookie過期則認證服務根本獲取不到Cookie信息),當訪問頁面的間隔大於10分鍾時,Cookie依然會失效,此時雖然Cookie值還在但已經是無意義的一段數據了,所以我們將這三個值options.ExpireTimeSpan,options.Cookie.Expiration ,options.Cookie.MaxAge設置為相同似乎更合理點。
總結
- 我們介紹了如何在項目中使用基於Cookie的授權認證。
- 我們介紹了如何配置認證方案以及認證方案的友好提示名稱,並介紹了如何獲取所有的認證方案展示到頁面中。
- 我們介紹了如何配置認證的滑動過期時間以及如何配置持久Cookie。
