ASP.NET Core的Data Protect(數據保護)的學習和應用

来源:https://www.cnblogs.com/zhiyong-ITNote/archive/2018/07/18/9332391.html
-Advertisement-
Play Games

轉載請註入出處: https://home.cnblogs.com/u/zhiyong-ITNote/ dotnet core中提供了一個新的身份驗證框架Identity,它不同於dot net下的身份驗證。在這個框架裡面,有一個生成token的功能,也就是我們常說的令牌,令牌的作用有哪些?Toke ...


轉載請註入出處: https://home.cnblogs.com/u/zhiyong-ITNote/

dotnet core中提供了一個新的身份驗證框架Identity,它不同於dot net下的身份驗證。在這個框架裡面,有一個生成token的功能,也就是我們常說的令牌,令牌的作用有哪些?
Token值介紹
token 值: 登錄令牌.利用 token 值來判斷用戶的登錄狀態.類似於 MD5 加密之後的長字元串.
用戶登錄成功之後,在後端(伺服器端)會根據用戶信息生成一個唯一的值.這個值就是 token 值.
基本使用:
在伺服器端(資料庫)會保存這個 token 值,以後利用這個 token 值來檢索對應的用戶信息,並且判斷用戶的登錄狀態.
用戶登錄成功之後,伺服器會將生成的 token 值返回給 客戶端,在客戶端也會保存這個 token 值.(一般可以保存在 cookie 中,也可以自己手動確定保存位置(比如偏好設置.)).
以後客戶端在發送新的網路請求的時候,會預設自動附帶這個 token 值(作為一個參數傳遞給伺服器.).伺服器拿到客戶端傳遞的 token 值跟保存在 資料庫中的 token 值做對比,以此來判斷用戶身份和登錄狀態.
判斷登錄狀態:
如果客戶端沒有這個 token 值,意味著沒有登錄成功過,提示用戶登錄.
如果客戶端有 token 值,一般會認為登錄成功.不需要用戶再次登錄(輸入賬號和密碼信息).
token 值擴展:
token 值有失效時間:
一般的 app ,token值得失效時間都在 1 年以上.
特殊的 app :銀行類 app /支付類 app :token值失效時間 15 分鐘左右.
一旦用戶信息改變(密碼改變),會在伺服器生成新的 token 值,原來的 token值就會失效.需要再次輸入賬號和密碼,以得到生成的新的 token 值.
唯一性判斷: 每次登錄,都會生成一個新的token值.原來的 token 值就會失效.利用時間來判斷登錄的差異性.


至此也就說完了其作用。那麼Identity框架是如何為我們提供這個能力的呢?我們又該如何使用呢?首先請你看下 asp.net core中的數據保護模塊,這是Identity框架實現token的基礎。
var token = await userManager.GenerateUserTokenAsync(user, "Default", "passwordless-auth");
我們通過這句代碼來生成token,userManager是Identity框架的用戶管理類UserManager的實例對象。

當然首先我們需要實現相關的依賴註入,我就不說了。Identity預設生成的token是基於DataProtectorTokenProvider類的,我們在依賴註入的時候,其實就引用了這個類,先看下AddDefaultTokenProviders的源碼:

public static IdentityBuilder AddDefaultTokenProviders(this IdentityBuilder builder)
{
var userType = builder.UserType;
var dataProtectionProviderType = typeof(DataProtectorTokenProvider<>).MakeGenericType(userType);
var phoneNumberProviderType = typeof(PhoneNumberTokenProvider<>).MakeGenericType(userType);
var emailTokenProviderType = typeof(EmailTokenProvider<>).MakeGenericType(userType);
var authenticatorProviderType = typeof(AuthenticatorTokenProvider<>).MakeGenericType(userType);
return builder.AddTokenProvider(TokenOptions.DefaultProvider, dataProtectionProviderType)
.AddTokenProvider(TokenOptions.DefaultEmailProvider, emailTokenProviderType)
.AddTokenProvider(TokenOptions.DefaultPhoneProvider, phoneNumberProviderType)
.AddTokenProvider(TokenOptions.DefaultAuthenticatorProvider, authenticatorProviderType);
}

然後我們看下DataProtectorTokenProvider類的部分源碼:

public class DataProtectorTokenProvider<TUser> : IUserTwoFactorTokenProvider<TUser> where TUser : class
{
/// <summary>
/// Initializes a new instance of the <see cref="DataProtectorTokenProvider{TUser}"/> class.
/// </summary>
/// <param name="dataProtectionProvider">The system data protection provider.</param>
/// <param name="options">The configured <see cref="DataProtectionTokenProviderOptions"/>.</param>
public DataProtectorTokenProvider(IDataProtectionProvider dataProtectionProvider, IOptions<DataProtectionTokenProviderOptions> options)
{
if (dataProtectionProvider == null)
{
throw new ArgumentNullException(nameof(dataProtectionProvider));
}
Options = options?.Value ?? new DataProtectionTokenProviderOptions();
// Use the Name as the purpose which should usually be distinct from others
Protector = dataProtectionProvider.CreateProtector(Name ?? "DataProtectorTokenProvider"); 
}

public virtual async Task<string> GenerateAsync(string purpose, UserManager<TUser> manager, TUser user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var ms = new MemoryStream();
var userId = await manager.GetUserIdAsync(user);
using (var writer = ms.CreateWriter())
{
writer.Write(DateTimeOffset.UtcNow);
writer.Write(userId);
writer.Write(purpose ?? "");
string stamp = null;
if (manager.SupportsUserSecurityStamp)
{
stamp = await manager.GetSecurityStampAsync(user);
}
writer.Write(stamp ?? "");
}
var protectedBytes = Protector.Protect(ms.ToArray());
return Convert.ToBase64String(protectedBytes);
}
public virtual async Task<bool> ValidateAsync(string purpose, string token, UserManager<TUser> manager, TUser user)
{
try
{
var unprotectedData = Protector.Unprotect(Convert.FromBase64String(token));
var ms = new MemoryStream(unprotectedData);
using (var reader = ms.CreateReader())
{
var creationTime = reader.ReadDateTimeOffset();
var expirationTime = creationTime + Options.TokenLifespan;
if (expirationTime < DateTimeOffset.UtcNow)
{
return false;
}

var userId = reader.ReadString();
var actualUserId = await manager.GetUserIdAsync(user);
if (userId != actualUserId)
{
return false;
}
var purp = reader.ReadString();
if (!string.Equals(purp, purpose))
{
return false;
}
var stamp = reader.ReadString();
if (reader.PeekChar() != -1)
{
return false;
}

if (manager.SupportsUserSecurityStamp)
{
return stamp == await manager.GetSecurityStampAsync(user);
}
return stamp == "";
}
}
// ReSharper disable once EmptyGeneralCatchClause
catch
{
// Do not leak exception
}
return false;
}
}
View Code

可以看到,預設的情況下,使用的就是asp.net core Data Provider生成token的。我們來看看這個DataProtectorTokenProviderOptions配置類的信息:

public class DataProtectionTokenProviderOptions
{
public string Name { get; set; } = "DataProtectorTokenProvider";

public TimeSpan TokenLifespan { get; set; } = TimeSpan.FromDays(1);
}

預設的情況下,token過期時間是一天,也就是說,如果我們需要修改過期時間的話,完全可以自己來:

services.Configure<DataProtectionTokenProviderOptions>(
x => x.TokenLifespan = TimeSpan.FromMinutes(15));

剛剛也說了,預設情況下Identity框架給我們提供的token是基於Data Protect的,那麼我們可以切換另外的token提供方式。只需繼承自TotpSecurityStampBasedTokenProvider<TUser>類就可以了。該類源碼地址:
https://github.com/aspnet/Identity/blob/c7276ce2f76312ddd7fccad6e399da96b9f6fae1/src/Core/TotpSecurityStampBasedTokenProvider.cs#L21 該類與DataProtectorTokenProvider類一樣都是繼承自IUserTwoFactorTokenProvider介面來實現的,其方法都是一樣的,只是該類的token生成演算法不再是Data Protect中的token生成演算法了,而是hash演算法,因此這裡也是一個可拓展點,我們還可以提供自己的想要的加密演算法來生成token。

public class PasswordlessLoginTotpTokenProvider<TUser> : TotpSecurityStampBasedTokenProvider<TUser> 
where TUser : class
{
public override Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TUser> manager, TUser user)
{
return Task.FromResult(false);
}

public override async Task<string> GetUserModifierAsync(string purpose, UserManager<TUser> manager, TUser user)
{
var email = await manager.GetEmailAsync(user);
return "PasswordlessLogin:" + purpose + ":" + email;
}
}

public static class CustomIdentityBuilderExtensions 
{
public static IdentityBuilder AddPasswordlessLoginTotpTokenProvider(this IdentityBuilder builder)
{
var userType = builder.UserType;
var totpProvider = typeof(PasswordlessLoginTotpTokenProvider<>).MakeGenericType(userType);
return builder.AddTokenProvider("PasswordlessLoginTotpProvider", totpProvider);
}
}

public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<IdentityDbContext>() 
.AddDefaultTokenProviders()
.AddPasswordlessLoginTotpTokenProvider(); // Add the custom token provider
}


var token = await userManager.GenerateUserTokenAsync( 
user, "PasswordlessLoginTotpProvider", "passwordless-auth");
View Code

這就是我們的使用了。在這裡提一下。TotpSecurityStampBasedTokenProvider類的預設生命周期是9分鐘,一旦超過9分鐘,token就會變化。

大概的理了下自己接下來的實現需要用到的技術,不能為了實現而實現,而是要想清楚需要的技術,同時去學習這些技術,然後再去實現。其實這個需求是我自己加的,因為之前看官方文檔的時候,對數據保護很懵逼,只知道可以用來生成token加密,卻不知道實際的用途,因此自己深入研究查找資料,借鑒別人的博客來學習,還是google好啊,百度真...就不爆粗口了。
整理下自己需要實現得需求思路:
我們可以在郵箱驗證的時候用到token,也可以在整個的網站中用到,第一個就先不說了,說說第二個:
我們一般可以將用戶登陸之後,根據用戶名什麼的生成token,然後放到服務端也就是資料庫了,我們給它有效期(比如連續記住我十天之類的)表示這個用戶已經登錄過了。那麼客戶端呢?也就是我們的瀏覽器,我們可以將cookie存入客戶端瀏覽器,但是一旦我們關閉了瀏覽器那麼cookie也就不存在了,因此不可行。可以存入到本地磁碟中。同時,一般來講,網站的url都很長,一般都是將token拼接在了url裡面,如果你覺得url太長了,我們可以在中間件中寫入到http頭部欄位中,根據http頭部欄位。當然你可以在ActionFilter以及ResultFilter中寫入http頭部。

回頭看看能不能整理出來一個實現,到時候再放上來代碼。

參考資料:

Implementing custom token providers for passwordless authentication in ASP.NET Core Identity

Implementing Medium's Passwordless Authentication using ASP.NET Core Identity

轉載請註入出處: https://home.cnblogs.com/u/zhiyong-ITNote/


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Description 給定n個正整數a1,a2,…,an,求 的值(答案模10^9+7)。 給定n個正整數a1,a2,…,an,求 的值(答案模10^9+7)。 Input 第一行一個正整數n。 接下來n行,每行一個正整數,分別為a1,a2,…,an。 第一行一個正整數n。 接下來n行,每行一個正 ...
  • 最近爬蟲,爬個貓眼都被封了IP。。 分享幾個常見的User-Agent吧,複製粘貼過來的,謝謝原創。 明天後天,就這周吧,把貓眼,巨潮資訊,陽光網爬一下,然後再爬幾個有漂亮mm的網站,後面再做一個自己的翻譯器。 時間是擠出來的。。 ...
  • 今天碰到一個場景,就是一個JavaBean,有些屬性的值需要去資料庫其他表中獲取,這樣就需要調用其他dao方法得到這個值,然後再set進去。 可是問題來了,如果需要用這種方式賦值的屬性特別多的話,一個一個set進去就需要寫很多set方法,代碼不僅冗餘,而且很麻煩。 於是就想通過反射機制去自動set值 ...
  • 一、Redis API支持 Python連接redis redis-py安裝方式 Python連接Redis redis連接分片集群 python連接redis sentinel Python String類型使用簡介 Python hash類型使用簡介 Python list類型使用簡介 Pyth ...
  • 如果爬蟲需要展現速度,我覺得就是去下載圖片吧,原本是想選擇去煎蛋那裡下載圖片的,那裡的美女圖片都是高質量的,我稿子都是差不多寫好了的,無奈今天重新看下,妹子圖的入口給關了。 至於為什麼關呢,大家可以去看看XXX日報的關停原因吧或者百度下,這裡就不多說了,這次我選擇了去下載無版權高清圖片,因為做自媒體 ...
  • 當需要向某特定URL地址發送HTTP請求並得到相應響應時,通常會用到HttpClient類。該類包含了眾多有用的方法,可以滿足絕大多數的需求。但是如果對其使用不當時,可能會出現意想不到的事情。 博客園官方團隊就遇上過這樣的 "問題" ,國外博主也記錄過類似的情況, "YOU'RE USING HTT ...
  • 1.第一步環境搭建 運行環境:window 客戶端版本:Go語言geth 下載地址https://ethereum.github.io/go-ethereum/downloads/ 以太坊API中文文檔:http://web3.tryblockchain.org/Web3.js-api-refren ...
  • 書本:https://www.tutorialspoint.com/nhibernate/index.htm 第一天學習內容 概念 Nhibernate是一個ORM框架。 ORM框架:將聲明的類映射到資料庫中。可以不使用SQL語言,減少錯誤。 Demo 1.創建空控制台應用 起名Nhibernate ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...