必讀 本文源碼核心邏輯使用 "AspNetCore.Totp" ,為什麼不使用 而是使用源碼封裝後面將會說明。 為了防止不提供原網址的轉載,特在這裡加上原文鏈接: https://www.cnblogs.com/yuefengkai/p/11408339.html 雙因素認證 雙因素身份認證就是通過 ...
必讀
本文源碼核心邏輯使用AspNetCore.Totp,為什麼不使用AspNetCore.Totp
而是使用源碼封裝後面將會說明。
為了防止不提供原網址的轉載,特在這裡加上原文鏈接:
https://www.cnblogs.com/yuefengkai/p/11408339.html
雙因素認證
雙因素身份認證就是通過你所知道再加上你所能擁有的這二個要素組合到一起才能發揮作用的身份認證系統。雙因素認證是一種採用時間同步技術的系統,採用了基於時間、事件和密鑰三變數而產生的一次性密碼來代替傳統的靜態密碼。每個動態密碼卡都有一個唯一的密鑰,該密鑰同時存放在伺服器端,每次認證時動態密碼卡與伺服器分別根據同樣的密鑰,同樣的隨機參數(時間、事件)和同樣的演算法計算了認證的動態密碼,從而確保密碼的一致性,從而實現了用戶的認證。就像我們去銀行辦卡送的口令牌.
一. 前言
最近公司內部SSO登錄一直在找一種安全的方式,目前已實現方案:賬號密碼登錄以及手機驗證碼登錄,通過Apollo切換不同的登錄方式,想起18年看到AspNetCore.Totp並也編寫了DemodotNetCore-2FA登錄,將之前寫的再完善並且在此記錄和分析,希望對大家有些幫助。
二. AspNetCore.Totp
說明一下為什麼要用AspNetCore.Totp
修改並且重新打包Brook.Totp
因AspNetCore.Totp
在生成二維碼鏈接時會訪問404(google.com)網站,國內基本無法使用,這很不清真,還有就是註入需要註入介面和實現類,使用起來很繁瑣,所以才萌生了讓使用起來更方便,並且不依賴Google生成二維碼
- 生成二維碼
accountIdentity = accountIdentity.Replace(" ", "");
var encodedSecretKey = Base32.Encode(accountSecretKey);
var provisionUrl = UrlEncoder.Encode(string.Format("otpauth://totp/{0}?secret={1}&issuer={2}", accountIdentity, encodedSecretKey, UrlEncoder.Encode(issuer)));
var protocol = useHttps ? "https" : "http";
var url = $"{protocol}://chart.googleapis.com/chart?cht=qr&chs={qrCodeWidth}x{qrCodeHeight}&chl={provisionUrl}";
var totpSetup = new TotpSetup
{
QrCodeImage = this.GetQrImage(url),
ManualSetupKey = encodedSecretKey
};
- 註入方式
Startup註入
services.AddSingleton<ITotpSetupGenerator, TotpSetupGenerator>();
services.AddSingleton<ITotpValidator, TotpValidator>();
services.AddSingleton<ITotpGenerator, TotpGenerator>();
Controller註入
private readonly ITotpGenerator _totpGenerator;
private readonly ITotpSetupGenerator _totpSetupGenerator;
private readonly ITotpValidator _totpValidator;
public ValuesController(ITotpSetupGenerator totpSetupGenerator)
{
_totpSetupGenerator = totpSetupGenerator;
_totpGenerator = new TotpGenerator();
_totpValidator = new TotpValidator(_totpGenerator);
}
三. Brook.Totp
- 二維碼使用
QRCoder
來生成,不依賴外部網路
/// <summary>
/// 生成二維碼
/// </summary>
/// <param name="provisionUrl"></param>
/// <param name="pixelsPerModule"></param>
/// <returns></returns>
private string GetQrBase64Imageg(string provisionUrl,int pixelsPerModule)
{
QRCodeGenerator qrGenerator = new QRCodeGenerator();
QRCodeData qrCodeData = qrGenerator.CreateQrCode(provisionUrl, QRCodeGenerator.ECCLevel.Q);
Base64QRCode qrCode = new Base64QRCode(qrCodeData);
string qrCodeImageAsBase64 = qrCode.GetGraphic(2);
return $"data:image/png;base64,{qrCodeImageAsBase64}";
}
- 註入方式
Startup註入
services.AddBrookTotp();
Controller註入
private readonly ITotp _totp;
public AccountController(ITotp totp)
{
_totp = totp;
}
四.雙因素APP
推薦使用Microsoft Authenticator支持IOS、安卓可自動備份
之前使用Google Authenticator手機壞了 Gitlab和DropBox 再也進不去了(心疼自己三秒鐘)
五. 完整流程效果圖
使用Microsoft Authenticator
- 正常登錄
- 登錄成功後綁定
- 綁定後再次登錄
六.如何使用
所有源代碼請參照我的GitHub https://github.com/yuefengkai/Brook.Totp
Demo中使用了
EF Core In Memory Database
所有的數據只存在記憶體中Cache in-memory
dotNET Core Authentication
下方只展示部分代碼
- 新建netCoreMVC項目添加Nuget包
Brook.Totp
- Startup註入
services.AddMemoryCache();
services.AddSingleton<ICacheManage, CacheManage>();
services.AddBrookTotp();
services.AddDbContext<BrookTotpDBContext>(options => options.UseInMemoryDatabase(databaseName: "BrookTotpDB"));
- Controller使用
private readonly ITotp _totp;
public AccountController(ITotp totp)
{
_totp = totp;
}
//獲取二維碼
[Authorize]
public IActionResult GetQr()
{
var totpSetup = _totp.GenerateUrl("dotNETBuild", CurremtUser.Email, CurremtUser.SecretKeyFor2FA);
return Json(new { qrCodeContennt = totpSetup.QrCodeImageContent });
}
//驗證雙因素校驗碼
[Authorize]
[HttpPost]
public async Task<IActionResult> Valid(int code)
{
var valid = _totp.Validate(CurremtUser.SecretKeyFor2FA
, code, 30);
if (!valid)
{
return Json(new { result = 0, msg = "2FA校驗失敗" });
}
//校驗成功後 如果是第一次綁定校驗 需將用戶的accountSecretKey 存入資料庫
CurremtUser.IsOpen2FA = true;
await _userService.UpdateAsync(CurremtUser);
_cacheManage.Remove(string.Format(CacheKeys.GetUserForEmail, CurremtUser.Email));
var claims = new List<Claim>
{
new Claim("user", CurremtUser.Email),
new Claim("role", "Member")
};
await HttpContext.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity(claims, "Cookies", "user", "role")));
return Json(new { result = 1, msg = "2FA校驗成功", url = "/Home/Index" });
}
七.寫在最後
以上所有源代碼已開源在 https://github.com/yuefengkai/Brook.Totp
如果覺得有用請給我個Start!
作者:Brook(高增智)