.NET Core 已經熱了好一陣子,1.1版本發布后其可用性也越來越高,開源、組件化、跨平臺、性能優秀、社區活躍等等標簽再加上“微軟爸爸”主推和大力支持,盡管現階段對比.net framework還是比較“稚嫩”,但可以想象到它光明的前景。作為.net 開發者你是否已經開始嘗試將項目遷移到.net core上?這其中要解決的一個較大的問題就是如何讓你的.net core和老.net framework站點實現身份驗證兼容!
1、第一篇章
我們先來看看.net core中對identity的實現,在Startup.cs的Configure中配置Cookie認證的相關屬性
1
2
3
4
5
6
7
8
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseCookieAuthentication( new CookieAuthenticationOptions { AuthenticationScheme = "test" , CookieName = "MyCookie" }); } |
Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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 視圖
1
2
3
4
5
6
7
8
9
10
11
|
<!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 視圖
1
2
3
4
5
6
7
8
9
10
11
12
|
<!DOCTYPE html> < html > < head > </ head > < body > @if (User.Identity.IsAuthenticated) { < p >登錄成功!</ p > } </ body > </ html > |
下面是實現效果的截圖:
ok,到此我們用.net core比較簡單地實現了用戶身份驗證信息的保存和讀取。
接著思考,如果我的.net framework項目想讀取.net core項目保存的身份驗證信息應該怎么做?
要讓兩個項目都接受同一個Identity至少需要三個條件:
- CookieName必須相同。
- Cookie的作用域名必須相同。
- 兩個項目的Cookie認證必須使用同一個Ticket。
首先我們對.net core的Cookie認證添加domain屬性和ticket屬性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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 }); } |
此時我們在.net core 項目中執行用戶登錄,程序會在我們指定的目錄下生成key.xml
我們打開文件看看程序幫我們記錄了那些信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<? 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,接下來我們開始配置.net framework項目,同樣,在Startup.cs中配置Cookie認證的相關屬性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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
1
2
3
4
5
6
7
8
9
10
11
12
|
<!DOCTYPE html> < html > < head > </ head > < body > @if (User.Identity.IsAuthenticated) { < p >.net framework登錄成功!</ p > } </ body > </ html > |
寫法和.net core 基本上是一致的,我們來看下能否成功獲取用戶名:
反之在.net framework中登錄在.net core中獲取身份驗證信息的方法是一樣的,這里就不重復寫了。
然而,到此為止事情就圓滿解決了嗎?很遺憾,麻煩才剛剛開始!
--------------------------------------------------------------------------------
2、第二篇章
如果你的子項目不多,也不復雜的情況下,新增一個.net core 站點,然后適當修改以前的.net framework站點,上述實例確實能夠滿足需求。可是如果你的子站點足夠多,或者項目太過復雜,牽扯到的業務過于龐大或重要,這種情況下我們通常是不愿意動老項目的。或者說我們沒有辦法將所有的項目都進行更改,然后和新增的.net core站點同時上線,如果這么做了,那么更新周期會拉的很長不說,測試和更新之后的維護階段壓力都會很大。所以我們必須要尋找到一種方案,讓.net core的身份驗證機制完全迎合.net framwork。
因為.net framework 的cookie是對稱加密,而.net core是非對稱加密,所以要在.net core中動手的話必須要對.net core 默認的加密和解密操作進行攔截,如果可行的話最好的方案應該是將.net framework的FormsAuthentication類移植到.net core中。但是用reflector看了下,牽扯到的代碼太多,剪不斷理還亂,github上也沒找到其源碼,瞎忙活了一陣之后終于感慨:臣妾做不到(>﹏< )。
Cookie認證的相關屬性
1
2
3
4
5
6
7
|
app.UseCookieAuthentication( new CookieAuthenticationOptions { AuthenticationScheme = "test" , CookieName = "MyCookie" , CookieDomain = "localhost" , TicketDataFormat = new FormsAuthTicketDataFormat( "" ) }); |
FormsAuthTicketDataFormat
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
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
1
2
3
4
5
6
|
public class FormsAuthTicket { public DateTime Expiration { get ; set ; } public DateTime IssueDate { get ; set ; } public string Name { get ; set ; } } |
以上實現了對cookie的解密攔截,然后通過webapi從.net framework獲取ticket
1
2
3
4
5
6
|
[Route( "getMyTicket" )] public IHttpActionResult GetMyTicket( string cookie) { var formsAuthTicket = FormsAuthentication.Decrypt(cookie); return Ok( new { formsAuthTicket.Name, formsAuthTicket.IssueDate, formsAuthTicket.Expiration }); } |
有了webapi這條線,解密解決了,加密就更簡單了,通過webapi獲取加密后的cookie,.net core要做的只有一步,保存cookie就行了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
[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
1
2
3
4
5
6
|
[Route( "getMyCookie" )] public string GetMyCookie( string name) { FormsAuthentication.SetAuthCookie(name, false ); return FormsAuthentication.GetAuthCookie(name, false ).Value; } |
其余代碼不用做任何更改,ok,我們來測試一下
ok,登錄成功,至此完成.net framework和.net core身份驗證的兼容,哎,如果.net core 的團隊能多考慮一些這方面的兼容問題,哪怕是一個折中方案也能讓開發者更有動力去做遷移。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://www.cnblogs.com/Jermey/p/6130324.html