詳解ASP.NET Core和ASP.NET Framework共享身份驗(yàn)證
.NET Core 已經(jīng)熱了好一陣子,1.1版本發(fā)布后其可用性也越來(lái)越高,開(kāi)源、組件化、跨平臺(tái)、性能優(yōu)秀、社區(qū)活躍等等標(biāo)簽再加上“微軟爸爸”主推和大力支持,盡管現(xiàn)階段對(duì)比.net framework還是比較“稚嫩”,但可以想象到它光明的前景。作為.net 開(kāi)發(fā)者你是否已經(jīng)開(kāi)始嘗試將項(xiàng)目遷移到.net core上?這其中要解決的一個(gè)較大的問(wèn)題就是如何讓你的.net core和老.net framework站點(diǎn)實(shí)現(xiàn)身份驗(yàn)證兼容!
1、第一篇章
我們先來(lái)看看.net core中對(duì)identity的實(shí)現(xiàn),在Startup.cs的Configure中配置Cookie認(rèn)證的相關(guān)屬性
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "test",
CookieName = "MyCookie"
});
}
Controller
public IActionResult Index()
{
return View();
}
public IActionResult Login()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Login(string name)
{
var identity = new ClaimsIdentity(
new List<Claim>
{
new Claim(ClaimTypes.Name,name, ClaimValueTypes.String)
},
ClaimTypes.Authentication,
ClaimTypes.Name,
ClaimTypes.Role);
var principal = new ClaimsPrincipal(identity);
var properties = new AuthenticationProperties { IsPersistent = true };
await HttpContext.Authentication.SignInAsync("test", principal, properties);
return RedirectToAction("Index");
}
login 視圖
<!DOCTYPE html>
<html>
<head>
<title>登錄</title>
</head>
<body>
<form asp-controller="Account" asp-action="Login" method="post">
<input type="text" name="name" /><input type="submit" value="提交" />
</form>
</body>
</html>
index 視圖
<!DOCTYPE html>
<html>
<head>
<title>歡迎您-@User.Identity.Name</title>
</head>
<body>
@if (User.Identity.IsAuthenticated)
{
<p>登錄成功!</p>
}
</body>
</html>
下面是實(shí)現(xiàn)效果的截圖:


ok,到此我們用.net core比較簡(jiǎn)單地實(shí)現(xiàn)了用戶身份驗(yàn)證信息的保存和讀取。
接著思考,如果我的.net framework項(xiàng)目想讀取.net core項(xiàng)目保存的身份驗(yàn)證信息應(yīng)該怎么做?
要讓兩個(gè)項(xiàng)目都接受同一個(gè)Identity至少需要三個(gè)條件:
- CookieName必須相同。
- Cookie的作用域名必須相同。
- 兩個(gè)項(xiàng)目的Cookie認(rèn)證必須使用同一個(gè)Ticket。
首先我們對(duì).net core的Cookie認(rèn)證添加domain屬性和ticket屬性
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
var protectionProvider = DataProtectionProvider.Create(new DirectoryInfo(@"C:\keyPath\"));
var dataProtector = protectionProvider.CreateProtector("MyCookieAuthentication");
var ticketFormat = new TicketDataFormat(dataProtector);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "test",
CookieName = "MyCookie",
CookieDomain = "localhost",
TicketDataFormat = ticketFormat
});
}
此時(shí)我們?cè)?net core 項(xiàng)目中執(zhí)行用戶登錄,程序會(huì)在我們指定的目錄下生成key.xml

我們打開(kāi)文件看看程序幫我們記錄了那些信息
<?xml version="1.0" encoding="utf-8"?>
<key id="eb8b1b59-dbc5-4a28-97ad-2117a2e8f106" version="1">
<creationDate>2016-12-04T08:27:27.8435415Z</creationDate>
<activationDate>2016-12-04T08:27:27.8214603Z</activationDate>
<expirationDate>2017-03-04T08:27:27.8214603Z</expirationDate>
<descriptor deserializerType="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
<descriptor>
<encryption algorithm="AES_256_CBC" />
<validation algorithm="HMACSHA256" />
<masterKey p4:requiresEncryption="true" xmlns:p4="http://schemas.asp.net/2015/03/dataProtection">
<value>yHdMEYlEBzcwpx0bRZVIbcGJ45/GqRwFjMfq8PJ+k7ZWsNMic0EMBgP33FOq9MFKX0XE/a1plhDizbb92ErQYw==</value>
</masterKey>
</descriptor>
</descriptor>
</key>
ok,接下來(lái)我們開(kāi)始配置.net framework項(xiàng)目,同樣,在Startup.cs中配置Cookie認(rèn)證的相關(guān)屬性。
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var protectionProvider = DataProtectionProvider.Create(new DirectoryInfo(@"C:\keyPath\"));
var dataProtector = protectionProvider.CreateProtector("MyCookieAuthentication");
var ticketFormat = new AspNetTicketDataFormat(new DataProtectorShim(dataProtector));
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "test",
CookieName = "MyCookie",
CookieDomain = "localhost",
TicketDataFormat = ticketFormat
});
}
}
view
<!DOCTYPE html>
<html>
<head>
<title>.net framewor歡迎您-@User.Identity.Name</title>
</head>
<body>
@if (User.Identity.IsAuthenticated)
{
<p>.net framework登錄成功!</p>
}
</body>
</html>
寫(xiě)法和.net core 基本上是一致的,我們來(lái)看下能否成功獲取用戶名:

反之在.net framework中登錄在.net core中獲取身份驗(yàn)證信息的方法是一樣的,這里就不重復(fù)寫(xiě)了。
然而,到此為止事情就圓滿解決了嗎?很遺憾,麻煩才剛剛開(kāi)始!
--------------------------------------------------------------------------------
2、第二篇章
如果你的子項(xiàng)目不多,也不復(fù)雜的情況下,新增一個(gè).net core 站點(diǎn),然后適當(dāng)修改以前的.net framework站點(diǎn),上述實(shí)例確實(shí)能夠滿足需求??墒侨绻愕淖诱军c(diǎn)足夠多,或者項(xiàng)目太過(guò)復(fù)雜,牽扯到的業(yè)務(wù)過(guò)于龐大或重要,這種情況下我們通常是不愿意動(dòng)老項(xiàng)目的。或者說(shuō)我們沒(méi)有辦法將所有的項(xiàng)目都進(jìn)行更改,然后和新增的.net core站點(diǎn)同時(shí)上線,如果這么做了,那么更新周期會(huì)拉的很長(zhǎng)不說(shuō),測(cè)試和更新之后的維護(hù)階段壓力都會(huì)很大。所以我們必須要尋找到一種方案,讓.net core的身份驗(yàn)證機(jī)制完全迎合.net framwork。
因?yàn)?net framework 的cookie是對(duì)稱加密,而.net core是非對(duì)稱加密,所以要在.net core中動(dòng)手的話必須要對(duì).net core 默認(rèn)的加密和解密操作進(jìn)行攔截,如果可行的話最好的方案應(yīng)該是將.net framework的FormsAuthentication類(lèi)移植到.net core中。但是用reflector看了下,牽扯到的代碼太多,剪不斷理還亂,github上也沒(méi)找到其源碼,瞎忙活了一陣之后終于感慨:臣妾做不到(>﹏< )。
Cookie認(rèn)證的相關(guān)屬性
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "test",
CookieName = "MyCookie",
CookieDomain = "localhost",
TicketDataFormat = new FormsAuthTicketDataFormat("")
});
FormsAuthTicketDataFormat
public class FormsAuthTicketDataFormat : ISecureDataFormat<AuthenticationTicket>
{
private string _authenticationScheme;
public FormsAuthTicketDataFormat(string authenticationScheme)
{
_authenticationScheme = authenticationScheme;
}
public AuthenticationTicket Unprotect(string protectedText, string purpose)
{
var formsAuthTicket = GetFormsAuthTicket(protectedText);
var name = formsAuthTicket.Name;
DateTime issueDate = formsAuthTicket.IssueDate;
DateTime expiration = formsAuthTicket.Expiration;
var claimsIdentity = new ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.Name, name) }, "Basic");
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
var authProperties = new Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties
{
IssuedUtc = issueDate,
ExpiresUtc = expiration
};
var ticket = new AuthenticationTicket(claimsPrincipal, authProperties, _authenticationScheme);
return ticket;
}
FormsAuthTicket GetFormsAuthTicket(string cookie)
{
return DecryptCookie(cookie).Result;
}
async Task<FormsAuthTicket> DecryptCookie(string cookie)
{
HttpClient _httpClient = new HttpClient();
var response = await _httpClient.GetAsync("http://192.168.190.134/user/getMyTicket?cookie={cookie}");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsAsync<FormsAuthTicket>();
}
}
FormsAuthTicket
public class FormsAuthTicket
{
public DateTime Expiration { get; set; }
public DateTime IssueDate { get; set; }
public string Name { get; set; }
}
以上實(shí)現(xiàn)了對(duì)cookie的解密攔截,然后通過(guò)webapi從.net framework獲取ticket
[Route("getMyTicket")]
public IHttpActionResult GetMyTicket(string cookie)
{
var formsAuthTicket = FormsAuthentication.Decrypt(cookie);
return Ok(new { formsAuthTicket.Name, formsAuthTicket.IssueDate, formsAuthTicket.Expiration });
}
有了webapi這條線,解密解決了,加密就更簡(jiǎn)單了,通過(guò)webapi獲取加密后的cookie,.net core要做的只有一步,保存cookie就行了
[HttpPost]
public async Task<IActionResult> Login(string name)
{
HttpClient _httpClient = new HttpClient();
var response = await _httpClient.GetAsync($"http://192.168.190.134/user/getMyCookie?name={name}");
response.EnsureSuccessStatusCode();
string cookieValue = (await response.Content.ReadAsStringAsync()).Trim('\"');
CookieOptions options = new CookieOptions();
options.Expires = DateTime.MaxValue;
HttpContext.Response.Cookies.Append("MyCookie", cookieValue, options);
return RedirectToAction("Index");
}
webapi獲取cookie
[Route("getMyCookie")]
public string GetMyCookie(string name)
{
FormsAuthentication.SetAuthCookie(name, false);
return FormsAuthentication.GetAuthCookie(name, false).Value;
}
其余代碼不用做任何更改,ok,我們來(lái)測(cè)試一下

ok,登錄成功,至此完成.net framework和.net core身份驗(yàn)證的兼容,哎,如果.net core 的團(tuán)隊(duì)能多考慮一些這方面的兼容問(wèn)題,哪怕是一個(gè)折中方案也能讓開(kāi)發(fā)者更有動(dòng)力去做遷移。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 詳解ASP.NET與ASP.NET Core用戶驗(yàn)證Cookie并存解決方案
- 基于ASP.NET Core數(shù)據(jù)保護(hù)生成驗(yàn)證token示例
- ASP.NET Core中實(shí)現(xiàn)用戶登錄驗(yàn)證的最低配置示例代碼
- ASP.NET Core如何添加統(tǒng)一模型驗(yàn)證處理機(jī)制詳解
- ASP.NET Core 2.0中Razor頁(yè)面禁用防偽令牌驗(yàn)證
- 在AspNetCore中使用極驗(yàn)做行為認(rèn)證的驗(yàn)證流程
- Asp.Net Core中基于Session的身份驗(yàn)證的實(shí)現(xiàn)
- ASP.NET Core使用自定義驗(yàn)證屬性控制訪問(wèn)權(quán)限詳解
相關(guān)文章
asp.net分頁(yè)控件使用詳解【附實(shí)例下載】
本篇文章主要對(duì)asp.net創(chuàng)建事務(wù)的方法進(jìn)行實(shí)例介紹,具有很好的參考價(jià)值,需要的朋友一起來(lái)看下吧2016-12-12
ASP.NET中用js取CheckBoxList中值的方法實(shí)例
用腳本取CheckBoxList中的值,并用"|"將其分開(kāi),之后將取到的值放入文本框,返回?cái)?shù)據(jù)庫(kù)做添加或者修改2013-07-07
比較簡(jiǎn)單的將數(shù)據(jù)信息導(dǎo)入wrod文檔方案(C# for word)
史上最簡(jiǎn)單將數(shù)據(jù)信息導(dǎo)入wrod文檔方案(C# for word)2010-01-01
剖析ASP.NET MVC的DependencyResolver組件
這篇文章主要為大家剖析ASP.NET MVC的DependencyResolver組件,感興趣的小伙伴們可以參考一下2016-04-04
Visual Stduio 2010開(kāi)發(fā)環(huán)境搭建教程
這篇文章主要為大家詳細(xì)介紹了Visual Stduio 2010開(kāi)發(fā)環(huán)境搭建教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
關(guān)于多對(duì)多關(guān)系表無(wú)法更新與插入的問(wèn)題
這篇文章主要介紹了關(guān)于多對(duì)多關(guān)系表無(wú)法更新與插入的問(wèn)題 的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07
ASP.NET 頁(yè)面刷新和定時(shí)跳轉(zhuǎn)代碼整理
很多是摘網(wǎng)上的但是我整理了一下。以便以后查閱。2009-12-12
ASP.NET MVC下的四種驗(yàn)證編程方式[續(xù)篇]
ASP.NET MVC支持四種服務(wù)端驗(yàn)證的編程方式(“手工驗(yàn)證”、“標(biāo)注ValidationAttribute特性”、“讓數(shù)據(jù)類(lèi)型實(shí)現(xiàn)IValidatableObject或者IDataErrorInfo”),那么在ASP.NET MVC框架內(nèi)部是如何提供針對(duì)這四種不同編程方式的支持的呢?本篇文章就來(lái)聊聊這背后的故事。2016-12-12
驗(yàn)證用戶必選CheckBox控件與自定義驗(yàn)證javascript代碼
CheckBox控件,由于它的值是選擇與非選擇。因此在提交數(shù)據(jù)時(shí),想讓用戶必須選擇CheckBox,普通情況之下,不好做驗(yàn)證;但我們可以使用asp:CustomValidator來(lái)驗(yàn)證,不過(guò)還得寫(xiě)自定義驗(yàn)證Javascript代碼2013-01-01

