.NET Core Identity Cookie SignIn
在上一篇的文章
我們已經了解 UserStore
與 UserManager
的使用場景,今天來學習 Identity 中驗證部份的邏輯
在上一篇的文章中有稍微提到 SignInManager
如名稱所示,我們可以猜到這個 Manager 是負責處理登入這部份的商業邏輯
閱讀原始碼之後發現 SignInManager
內部也有注入 UserManager
方法,代表 SignInManager
也只更高階的封裝 Github
至於該怎麼使用可以先參考 Microsoft.AspNetCore.Identity.UI
這個 Package 之前有提到裡面有預設的 Razor 頁面可以直接供我們參考 Github
此 Login 頁面中的 OnPostAsync
方法,會在我們按下 Button 後執行
- 使用
SignInManager.GetExternalAuthenticationSchemesAsync
檢查系統之中使否有設定第三方登入(Google),有的話就在底下顯示按鈕 - 使用
SignInManager.PasswordSignInAsync
輸入帳號密碼登入 - 檢查登入回傳結果與是否有開啟二次驗證
- 轉跳回原網址
得知了 SignInManager
不只支援一般的帳號密碼登入同時也支援第三方登入,
那目前的任務就是查看微軟官方提供了多少種登入方式給我們直接使用,我們可以在 Github 上面找到
發現微軟提供了 Cookies
、JwtBearer
等常用的驗證方式,與 Google、Twitter、Facebook 等等外部登入驗證。
那麼該怎麼告訴系統我們想要使用 Cookies
進行驗證呢?
先參考 Github 上的 Sample 學習一下該如何進行註冊
- 使用
AddAuthentication
方法將 Cookie AuthenticationScheme 註冊到 Services 之中 - 使用
AddCookie
方法設定 Cookie 的過期時間與更新規則 - 使用
UseAuthentication
方法指定流程需要使用 AuthenticationMiddleware
學習後回到我們自己的專案上手動註冊試試,為了方便測試這裡把 RequireConfirmedAccount
移除了,否則就需要實做 IUserEmailStore<IdentityUser>
介面。
builder.Services
.AddAuthentication()
.AddCookie(IdentityConstants.ApplicationScheme,options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
});
builder.Services.AddIdentityCore<IdentityUser>()
.AddUserStore<CustomUserStore>()
.AddSignInManager<SignInManager<IdentityUser>>();
app.UseAuthentication();
app.UseAuthorization();
在 AccountController
添加新方法
[HttpPost(template: "~/signin", Name = "SignIn")]
public async Task<SignInResult> SignIn(string userName, string password)
{
return await _signInManager.PasswordSignInAsync(
userName,
password,
false,
false);
}
在測試之前需要在我們的 CustomUserStore
實現 IUserPasswordStore
界面,因為 SignInManager
內部需要設定密碼 Hash,使用 EFCore 可以跳過這步驟
public Task SetPasswordHashAsync(IdentityUser user, string? passwordHash, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (user == null)
{
throw new ArgumentNullException(nameof(user), $"Parameter {nameof(user)} cannot be null.");
}
if (passwordHash == null)
{
throw new ArgumentNullException(nameof(passwordHash), $"Parameter {nameof(passwordHash)} cannot be null.");
}
user.PasswordHash = passwordHash;
return Task.CompletedTask;
}
public Task<string?> GetPasswordHashAsync(IdentityUser user, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (user == null)
{
throw new ArgumentNullException(nameof(user), $"Parameter {nameof(user)} cannot be null.");
}
return Task.FromResult(user.PasswordHash);
}
public Task<bool> HasPasswordAsync(IdentityUser user, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (user == null)
{
throw new ArgumentNullException(nameof(user), $"Parameter {nameof(user)} cannot be null.");
}
return Task.FromResult(!string.IsNullOrEmpty(user.PasswordHash));
}
由於之前建立的 User 都是沒有密碼的,所以回到 AccountController
在註冊時加上密碼參數。
[HttpPost("CreateUser")]
public async Task<IdentityResult> CreateUser(string userName, string password)
{
var identityUser = new IdentityUser(userName);
return await _userManager.CreateAsync(identityUser, password);
}
接下來 Post https://localhost:7057/Account/CreateUser?userName=user3&password=1q2w3E*
建立 user3
會員
查詢會發現 passwordHash
會在 UserManager.CreateAsync
執行的時候幫你設定好
{
"id": "5ad20e10-554d-45c4-842a-30e6ba4f65d7",
"userName": "user3",
"normalizedUserName": "USER3",
"email": null,
"normalizedEmail": null,
"emailConfirmed": false,
"passwordHash": "AQAAAAIAAYagAAAAEH0wEDNdxTqAnamzFKBRjW1fXrJxDeRMQnDnFVdcnoffz/uoeodhXosLAcFQtqRFGw==",
"securityStamp": "c22286d1-d45d-44fa-a76b-159771fe5d09",
"concurrencyStamp": "31c7bd93-4877-4fd6-aa6d-d839700817cf",
"phoneNumber": null,
"phoneNumberConfirmed": false,
"twoFactorEnabled": false,
"lockoutEnd": null,
"lockoutEnabled": false,
"accessFailedCount": 0
}
完成之後 Post 新的 API https://localhost:7057/Account/SignIn?userName=user3&password=1q2w3E*
進行登入,執行後會回傳以下內容
Response body
{
"succeeded": true,
"isLockedOut": false,
"isNotAllowed": false,
"requiresTwoFactor": false
}
看到 "succeeded": true 就代表登入成功了,此時可以檢查瀏覽器的 Cookie
localhost 域名底下會發現多了一個 Cookie .AspNetCore.Identity.Application
,這個 Cookie 就是 dotnet 預設的登入 Cookie 名稱
我們在 GetUser
方法新增驗證,代表需要登入才能取得會員詳細資訊
[Authorize]
[HttpGet("GetUser")]
public async Task<IdentityUser?> GetUser(string userName)
{
return await _userManager.FindByNameAsync(userName);
}
會發現沒登入直接呼叫會回傳錯誤,只有先登入後呼叫查詢才會正常回傳資訊
Summary
今天學習了 SignInManager
與使用 Cookie 來進行登入驗證,並且知道微軟已經內建提供了許多登入方式(Schema)
之後的文章會繼續試試看 JWT 或者第三方登入。