一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - ASP.NET教程 - ASP.NET Cookie是怎么生成的(推薦)

ASP.NET Cookie是怎么生成的(推薦)

2020-06-27 15:53.NET騷操作 ASP.NET教程

這篇文章主要介紹了ASP.NET Cookie是怎么生成的,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下

可能有人知道Cookie的生成由machineKey有關,machineKey用于決定Cookie生成的算法和密鑰,并如果使用多臺服務器做負載均衡時,必須指定一致的machineKey用于解密,那么這個過程到底是怎樣的呢?

如果需要在.NET Core中使用ASP.NET Cookie,本文將提到的內容也將是一些必經之路。

抽絲剝繭,一步一步分析
首先用戶通過AccountController->Login進行登錄:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//
// POST: /Account/Login
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
 if (!ModelState.IsValid)
 {
 return View(model);
 }
 
 var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
 switch (result)
 {
 case SignInStatus.Success:
  return RedirectToLocal(returnUrl);
 // ......省略其它代碼
 }
}

它調用了SignInManager的PasswordSignInAsync方法,該方法代碼如下(有刪減):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public virtual async Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
{
 // ...省略其它代碼
 if (await UserManager.CheckPasswordAsync(user, password).WithCurrentCulture())
 {
 if (!await IsTwoFactorEnabled(user))
 {
  await UserManager.ResetAccessFailedCountAsync(user.Id).WithCurrentCulture();
 }
 return await SignInOrTwoFactor(user, isPersistent).WithCurrentCulture();
 }
 // ...省略其它代碼
 return SignInStatus.Failure;
}

想瀏覽原始代碼,可參見官方的Github鏈接:

https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Owin/SignInManager.cs#L235-L276

可見它先需要驗證密碼,密碼驗證正確后,它調用了SignInOrTwoFactor方法,該方法代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
private async Task<SignInStatus> SignInOrTwoFactor(TUser user, bool isPersistent)
{
 var id = Convert.ToString(user.Id);
 if (await IsTwoFactorEnabled(user) && !await AuthenticationManager.TwoFactorBrowserRememberedAsync(id).WithCurrentCulture())
 {
 var identity = new ClaimsIdentity(DefaultAuthenticationTypes.TwoFactorCookie);
 identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, id));
 AuthenticationManager.SignIn(identity);
 return SignInStatus.RequiresVerification;
 }
 await SignInAsync(user, isPersistent, false).WithCurrentCulture();
 return SignInStatus.Success;
}

該代碼只是判斷了是否需要做雙重驗證,在需要雙重驗證的情況下,它調用了AuthenticationManager的SignIn方法;否則調用SignInAsync方法。SignInAsync的源代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public virtual async Task SignInAsync(TUser user, bool isPersistent, bool rememberBrowser)
{
 var userIdentity = await CreateUserIdentityAsync(user).WithCurrentCulture();
 // Clear any partial cookies from external or two factor partial sign ins
 AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie, DefaultAuthenticationTypes.TwoFactorCookie);
 if (rememberBrowser)
 {
 var rememberBrowserIdentity = AuthenticationManager.CreateTwoFactorRememberBrowserIdentity(ConvertIdToString(user.Id));
 AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = isPersistent }, userIdentity, rememberBrowserIdentity);
 }
 else
 {
 AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = isPersistent }, userIdentity);
 }
}

可見,最終所有的代碼都是調用了AuthenticationManager.SignIn方法,所以該方法是創建Cookie的關鍵。

AuthenticationManager的實現定義在Microsoft.Owin中,因此無法在ASP.NET Identity中找到其源代碼,因此我們打開Microsoft.Owin的源代碼繼續跟蹤(有刪減):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void SignIn(AuthenticationProperties properties, params ClaimsIdentity[] identities)
{
 AuthenticationResponseRevoke priorRevoke = AuthenticationResponseRevoke;
 if (priorRevoke != null)
 {
 // ...省略不相關代碼
 AuthenticationResponseRevoke = new AuthenticationResponseRevoke(filteredSignOuts);
 }
 
 AuthenticationResponseGrant priorGrant = AuthenticationResponseGrant;
 if (priorGrant == null)
 {
 AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(identities), properties);
 }
 else
 {
 // ...省略不相關代碼
 
 AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(mergedIdentities), priorGrant.Properties);
 }
}

AuthenticationManager的Github鏈接如下:https://github.com/aspnet/AspNetKatana/blob/c33569969e79afd9fb4ec2d6bdff877e376821b2/src/Microsoft.Owin/Security/AuthenticationManager.cs

可見它用到了AuthenticationResponseGrant,繼續跟蹤可以看到它實際是一個屬性:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public AuthenticationResponseGrant AuthenticationResponseGrant
{
 // 省略get
 set
 {
 if (value == null)
 {
  SignInEntry = null;
 }
 else
 {
  SignInEntry = Tuple.Create((IPrincipal)value.Principal, value.Properties.Dictionary);
 }
 }
}

發現它其實是設置了SignInEntry,繼續追蹤:

?
1
2
3
4
5
public Tuple<IPrincipal, IDictionary<string, string>> SignInEntry
{
 get { return _context.Get<Tuple<IPrincipal, IDictionary<string, string>>>(OwinConstants.Security.SignIn); }
 set { _context.Set(OwinConstants.Security.SignIn, value); }
}

其中,_context的類型為IOwinContext,OwinConstants.Security.SignIn的常量值為"security.SignIn"。

跟蹤完畢……

啥?跟蹤這么久,居然跟丟啦!?
當然沒有!但接下來就需要一定的技巧了。

原來,ASP.NET是一種中間件(Middleware)模型,在這個例子中,它會先處理MVC中間件,該中間件處理流程到設置AuthenticationResponseGrant/SignInEntry為止。但接下來會繼續執行CookieAuthentication中間件,該中間件的核心代碼在aspnet/AspNetKatana倉庫中可以看到,關鍵類是CookieAuthenticationHandler,核心代碼如下:

 

?
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
protected override async Task ApplyResponseGrantAsync()
{
 AuthenticationResponseGrant signin = Helper.LookupSignIn(Options.AuthenticationType);
 // ... 省略部分代碼
 
 if (shouldSignin)
 {
 var signInContext = new CookieResponseSignInContext(
  Context,
  Options,
  Options.AuthenticationType,
  signin.Identity,
  signin.Properties,
  cookieOptions);
 
 // ... 省略部分代碼
 
 model = new AuthenticationTicket(signInContext.Identity, signInContext.Properties);
 // ... 省略部分代碼
 
 string cookieValue = Options.TicketDataFormat.Protect(model);
 
 Options.CookieManager.AppendResponseCookie(
  Context,
  Options.CookieName,
  cookieValue,
  signInContext.CookieOptions);
 }
 // ... 又省略部分代碼
}

這個原始函數有超過200行代碼,這里我省略了較多,但保留了關鍵、核心部分,想查閱原始代碼可以移步Github鏈接:https://github.com/aspnet/AspNetKatana/blob/0fc4611e8b04b73f4e6bd68263e3f90e1adfa447/src/Microsoft.Owin.Security.Cookies/CookieAuthenticationHandler.cs#L130-L313

這里挑幾點最重要的講。

與MVC建立關系

建立關系的核心代碼就是第一行,它從上文中提到的位置取回了AuthenticationResponseGrant,該Grant保存了Claims、AuthenticationTicket等Cookie重要組成部分:

AuthenticationResponseGrant signin = Helper.LookupSignIn(Options.AuthenticationType);
繼續查閱LookupSignIn源代碼,可看到,它就是從上文中的AuthenticationManager中取回了AuthenticationResponseGrant(有刪減):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public AuthenticationResponseGrant LookupSignIn(string authenticationType)
{
 // ...
 AuthenticationResponseGrant grant = _context.Authentication.AuthenticationResponseGrant;
 // ...
 
 foreach (var claimsIdentity in grant.Principal.Identities)
 {
 if (string.Equals(authenticationType, claimsIdentity.AuthenticationType, StringComparison.Ordinal))
 {
  return new AuthenticationResponseGrant(claimsIdentity, grant.Properties ?? new AuthenticationProperties());
 }
 }
 
 return null;
}

如此一來,柳暗花明又一村,所有的線索就立即又明朗了。

Cookie的生成

從AuthenticationTicket變成Cookie字節串,最關鍵的一步在這里:

string cookieValue = Options.TicketDataFormat.Protect(model);
在接下來的代碼中,只提到使用CookieManager將該Cookie字節串添加到Http響應中,翻閱CookieManager可以看到如下代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void AppendResponseCookie(IOwinContext context, string key, string value, CookieOptions options)
{
 if (context == null)
 {
 throw new ArgumentNullException("context");
 }
 if (options == null)
 {
 throw new ArgumentNullException("options");
 }
 
 IHeaderDictionary responseHeaders = context.Response.Headers;
 // 省去“1萬”行計算chunk和處理細節的流程
 responseHeaders.AppendValues(Constants.Headers.SetCookie, chunks);
}

有興趣的朋友可以訪問Github看原始版本的代碼:https://github.com/aspnet/AspNetKatana/blob/0fc4611e8b04b73f4e6bd68263e3f90e1adfa447/src/Microsoft.Owin/Infrastructure/ChunkingCookieManager.cs#L125-L215

可見這個實現比較……簡單,就是往Response.Headers中加了個頭,重點只要看TicketDataFormat.Protect方法即可。

逐漸明朗

該方法源代碼如下:

?
1
2
3
4
5
6
7
public string Protect(TData data)
{
 byte[] userData = _serializer.Serialize(data);
 byte[] protectedData = _protector.Protect(userData);
 string protectedText = _encoder.Encode(protectedData);
 return protectedText;
}

可見它依賴于_serializer、_protector、_encoder三個類,其中,_serializer的關鍵代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public virtual byte[] Serialize(AuthenticationTicket model)
{
 using (var memory = new MemoryStream())
 {
 using (var compression = new GZipStream(memory, CompressionLevel.Optimal))
 {
  using (var writer = new BinaryWriter(compression))
  {
  Write(writer, model);
  }
 }
 return memory.ToArray();
 }
}

其本質是進行了一次二進制序列化,并緊接著進行了gzip壓縮,確保Cookie大小不要失去控制(因為.NET的二進制序列化結果較大,并且微軟喜歡搞xml,更大

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 极品91| 小妇人电影免费完整观看2021 | 好男人好资源在线观看免费 | 久久精品中文闷骚内射 | 色偷偷亚洲综合网亚洲 | 成人男女啪啪免费观看网站 | free性俄罗斯护士 | 韩剧在线免费观看 | 99热久久这里只精品国产www | bbc japanese黑人强行 | 四虎影视在线观看永久地址 | 亚洲午夜精品久久久久 | 欧美图片小说 | 久热在线这里只有精品7 | 给我免费观看的视频在线播放 | 国产欧美日韩免费一区二区 | 日韩 国产 欧美 | 亚洲男人的天堂网站 | 男女交性特一级 | dyav午夜片| 99r视频在线观看 | 沟厕okn系列在线播放 | 精品久久久久久久久免费影院 | 动漫美女被羞羞产奶 | 国产成人精品一区二区仙踪林 | 美女翘臀内疯狂进出 | 亚洲精品国精品久久99热 | 亚洲国产精品综合一区在线 | 女八把屁股扒开让男生添 | 国产精品久久久久久福利 | 九九热在线观看视频 | 青青青手机在线观看 | 国产亚洲精品九九久在线观看 | 国产精品亚洲一区二区 | 国产在线98福利播放视频免费 | 五月色综合婷婷综合俺来也 | 国产hd老太婆 | 色视频久久 | 欧美最新在线 | 亚洲国产无线码在线观看 | 久久国产精品免费网站 |