ASP.NET 쿠키 는 어떻게 생 성 되 나 요(추천)

16922 단어 ASP.NETCookie
쿠키 생 성 이 machineKey 와 관련 이 있다 는 것 을 아 는 사람 이 있 을 수 있 습 니 다.machineKey 는 쿠키 생 성 알고리즘 과 키 를 결정 하 는 데 사용 되 며,여러 대의 서버 를 부하 균형 으로 사용 할 때 일치 하 는 machineKey 를 복호화 에 사용 하도록 지정 해 야 합 니 다.이 과정 은 도대체 어떻게 되 는 것 일 까요?
.NET Core 에서 ASP.NET Cookie 를 사용 해 야 한다 면 본 고 에서 언급 할 내용 도 필수 적 인 길이 될 것 이다.
실 을 뽑 아 고 치 를 벗 기 고 한 걸음 한 걸음 분석 하 다.
우선 사용 자 는 AccountController->Login 을 통 해 로그 인 합 니 다.

//
// 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 방법 을 호출 했 습 니 다.이 방법 코드 는 다음 과 같 습 니 다(삭제 되 었 습 니 다).

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
암 호 를 먼저 검증 해 야 한 다 는 것 을 알 수 있 습 니 다.암호 검증 이 정확 한 후에 SignInOrTwo Factor 방법 을 호출 했 습 니 다.이 방법 코드 는 다음 과 같 습 니 다.

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 의 소스 코드 는 다음 과 같 습 니 다.

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 의 소스 코드 를 열 어 계속 추적 합 니 다(삭제 되 었 습 니 다).

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
Authentication Response Grant 를 사용 한 것 을 알 수 있 습 니 다.계속 추적 하면 실제 속성 을 볼 수 있 습 니 다.

public AuthenticationResponseGrant AuthenticationResponseGrant
{
 //   get
 set
 {
 if (value == null)
 {
  SignInEntry = null;
 }
 else
 {
  SignInEntry = Tuple.Create((IPrincipal)value.Principal, value.Properties.Dictionary);
 }
 }
}
이것 은 사실 SignInEntry 를 설 치 했 음 을 발견 하고 계속 추적 합 니 다.

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 미들웨어 를 먼저 처리 합 니 다.이 미들웨어 처리 절 차 는 Authentication Response Grant/SignInEntry 를 설정 할 때 까지 입 니 다.그러나 다음 에 CookieAuthentication 미들웨어 를 계속 실행 할 것 입 니 다.이 미들웨어 의 핵심 코드 는 aspnet/AspnetKatana 창고 에서 볼 수 있 습 니 다.관건 적 인 종 류 는 CookieAuthentication Handler 입 니 다.핵심 코드 는 다음 과 같 습 니 다.

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 와 관 계 를 맺다
관계 구축 의 핵심 코드 는 첫 번 째 줄 입 니 다.위 에서 언급 한 위치 에서 Authentication Response Grant 를 되 찾 았 습 니 다.이 Grant 는 Claims,AuthenticationTicket 등 쿠키 의 중요 한 구성 부분 을 저장 합 니 다.
AuthenticationResponseGrant signin = Helper.LookupSignIn(Options.AuthenticationType);
LookupSignIn 소스 코드 를 계속 찾 아 보면 위의 AuthenticationManager 에서 AuthenticationResponse Grant 를 되 찾 았 습 니 다.(삭제 되 었 습 니 다)

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;
}
이렇게 되자 유암 화 명 은 또 한 마을 에 있 고 모든 단 서 는 곧 밝 아 졌 다.
쿠키 생 성
AuthenticationTicket 에서 Cookie 바이트 문자열 로 바 뀌 었 습 니 다.가장 중요 한 단 계 는 다음 과 같 습 니 다.
string cookieValue = Options.TicketDataFormat.Protect(model);
다음 코드 에 서 는 CookieManager 를 사용 하여 이 Cookie 바이트 문자열 을 Http 응답 에 추가 하 는 것 만 언급 합 니 다.CookieManager 를 뒤 져 보면 다음 코드 를 볼 수 있 습 니 다.

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
이 를 통 해 알 수 있 듯 이 이 실현 은 비교적......................................................................
점점 밝 아 져
이 방법의 원본 코드 는 다음 과 같다.

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 의 핵심 코드 는 다음 과 같 습 니 다.

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 압축 을 실시 하여 쿠키 크기 가 통 제 력 을 잃 지 않도록 확보 하 는 것 이다(.NET 의 바 이 너 리 서열 화 결과 가 비교적 크 고 마이크로소프트 는 xml 를 좋아 하기 때문에 더욱 크다.😂)。
그리고 보 겠 습 니 다encoder 소스 코드:

public string Encode(byte[] data)
{
 if (data == null)
 {
 throw new ArgumentNullException("data");
 }

 return Convert.ToBase64String(data).TrimEnd('=').Replace('+', '-').Replace('/', '_');
}
이 를 통 해 알 수 있 듯 이 간단 한 base64-url 인 코딩 을 진 행 했 습 니 다.이 인 코딩 은=번 호 를 삭 제 했 기 때문에 base64-url 인 코딩 을 할 때=번 호 를 보충 해 야 합 니 다.
이 두 가 지 는 모두 비교적 간단 하 다.약간 복잡 한 것 은 이다.protector,그것 의 유형 은 IDataProtector 입 니 다.
IDataProtector
쿠키 Authentication Middleware 에서 초기 화 되 었 습 니 다.코드 와 인 자 는 다음 과 같 습 니 다.

IDataProtector dataProtector = app.CreateDataProtector(
 typeof(CookieAuthenticationMiddleware).FullName,
 Options.AuthenticationType, "v1");
세 개의 인 자 를 전 달 했 습 니 다.첫 번 째 인 자 는 CookieAuthenticationMiddleware 의 FullName 입 니 다.즉,"Microsoft.Owin.security.Cookies.CookieAuthenticationMiddleware"입 니 다.두 번 째 인 자 는 정의 되 지 않 으 면 기본 값 은 CookieAuthenticationDefaults.AuthenticationType 입 니 다.이 값 은"Cookies"로 정 의 됩 니 다.
단,기본 으로 생 성 된 ASP.NET MVC 템 플 릿 항목 에서 이 값 은 ASP.NET Identity 의 기본 값 인'ApplicationCookie'로 재 정의 되 므 로 주의해 야 합 니 다.
그리고 CreateDataProtector 의 소스 코드 를 살 펴 보 겠 습 니 다.

public static IDataProtector CreateDataProtector(this IAppBuilder app, params string[] purposes)
{
 if (app == null)
 {
 throw new ArgumentNullException("app");
 }

 IDataProtectionProvider dataProtectionProvider = GetDataProtectionProvider(app);
 if (dataProtectionProvider == null)
 {
 dataProtectionProvider = FallbackDataProtectionProvider(app);
 }
 return dataProtectionProvider.Create(purposes);
}

public static IDataProtectionProvider GetDataProtectionProvider(this IAppBuilder app)
{
 if (app == null)
 {
 throw new ArgumentNullException("app");
 }
 object value;
 if (app.Properties.TryGetValue("security.DataProtectionProvider", out value))
 {
 var del = value as DataProtectionProviderDelegate;
 if (del != null)
 {
  return new CallDataProtectionProvider(del);
 }
 }
 return null;
}
이 를 통 해 알 수 있 듯 이 IAppBuilder 의"security.DataProtection Provider"속성 에서 IDataProtection Provider 를 가 져 옵 니 다.그렇지 않 으 면 DpapiDataProtection Provider 를 사용 합 니 다.
코드 를 뒤 져 보면 OwinAppContext 에서 볼 수 있 습 니 다.이 값 은 MachineKeyDataProtection Provider 로 지정 되 었 습 니 다.
builder.Properties[Constants.SecurityDataProtectionProvider] = new MachineKeyDataProtectionProvider().ToOwinFunction();
글 속 의 Constants.Security DataProtection Provider 는 마침'security.DataProtection Provider'로 정의 되 었 다.
우 리 는 MachineKeyDataProtector 의 소스 코드 를 뒤 져 보 았 는데,마침 MachineKey 에 의존 하 는 것 을 보 았 다.

internal class MachineKeyDataProtector
{
 private readonly string[] _purposes;

 public MachineKeyDataProtector(params string[] purposes)
 {
 _purposes = purposes;
 }

 public virtual byte[] Protect(byte[] userData)
 {
 return MachineKey.Protect(userData, _purposes);
 }

 public virtual byte[] Unprotect(byte[] protectedData)
 {
 return MachineKey.Unprotect(protectedData, _purposes);
 }
}
결국 우리 의 오 랜 친구 인 Machine Key 에 도착 했다.
역 추진 과정,쿠키 해독
먼저 이 과정 을 정리 하고 Mvc 에서 요청 한 절차 에 있어 이 코드 들 은 ASP.NET Identity 에 집중 되 어 있 습 니 다.이 과정 은 다음 과 같 습 니 다.
  • AccountController
  • SignInManager
  • AuthenticationManager
  • 인증 응답 부여 설정
    그리고 CookieAuthentication 프로 세 스에 들 어 갑 니 다.이 코드 들 은 Owin 에 집중 되 어 있 습 니 다.
    CookieAuthenticationMiddleware(AuthenticationResponseGrant 읽 기)
    ISecureDataFormat(구현 클래스:SecureDataFormat)
    IDataSerializer(구현 클래스:TicketSerializer)
    IDataProtector(구현 클래스:MachineKeyDataProtector)
    ITextEncoder(구현 클래스:Base64UrlTextEncoder)
    이런 과정 에서 상기 에서 찾 은 모든 매개 변수의 값 은 제 가 정리 한'조상 해독 코드'는 다음 과 같 습 니 다.
    
    string cookie = "nZBqV1M-Az7yJezhb6dUzS_urj1urB0GDufSvDJSa0pv27CnDsLHRzMDdpU039j6ApL-VNfrJULfE85yU9RFzGV_aAGXHVkGckYqkCRJUKWV8SqPEjNJ5ciVzW--uxsCBNlG9jOhJI1FJIByRzYJvidjTYABWFQnSSd7XpQRjY4lb082nDZ5lwJVK3gaC_zt6H5Z1k0lUFZRb6afF52laMc___7BdZ0mZSA2kRxTk1QY8h2gQh07HqlR_p0uwTFNKi0vW9NxkplbB8zfKbfzDj7usep3zAeDEnwofyJERtboXgV9gIS21fLjc58O-4rR362IcCi2pYjaKHwZoO4LKWe1bS4r1tyzW0Ms-39Njtiyp7lRTN4HUHMUi9PxacRNgVzkfK3msTA6LkCJA3VwRm_UUeC448Lx5pkcCPCB3lGat_5ttGRjKD_lllI-YE4esXHB5eJilJDIZlEcHLv9jYhTl17H0Jl_H3FqXyPQJR-ylQfh";
    var bytes = TextEncodings.Base64Url.Decode(cookie);
    var decrypted = MachineKey.Unprotect(bytes,
     "Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware",
     "ApplicationCookie",
     "v1");
    var serializer = new TicketSerializer();
    var ticket = serializer.Deserialize(decrypted);
    ticket.Dump(); // Dump LINQPad    ,        ,           
    실행 하기 전에 app.config/web.config 의 machineKey 노드 를 설정 하고 NuGet 패키지:Microsoft.Owin.security 를 설치 하 십시오.실행 결 과 는 다음 과 같 습 니 다(완벽 한 해결).

    총결산
    학습 방식 은 여러 가지 가 있 는데 그 중에서 코드 를 보 는 것 은 제 가 개인 적 으로 매우 좋아 하 는 방식 입 니 다.모든 코드 가 평탄 한 것 은 아 닙 니 다.이 예 와 같이 ASP.NET 지식 배경 이 필요 할 수도 있 습 니 다.
    이'조상 코드'는.NET Framework 를 기반 으로 한 것 으로 MachineKey 를 사 용 했 기 때문에.NET Core 에서 실행 할 수 없습니다.저 는 잠시 후에 MachineKey 라 는 종 류 를 깊이 있 게 이야기 할 것 입 니 다.바 텀 코드 가 어떻게 작 동 하 는 지 본 다음 에.NET Core 에서 ASP.NET Identity 중의 Cookie 를 직접 풀 수 있 습 니 다.기대 하 세 요!
    위 에서 기술 한 것 은 편집장 이 여러분 에 게 소개 한 ASP.NET Cookie 가 어떻게 생 성 되 었 는 지 여러분 에 게 도움 이 되 기 를 바 랍 니 다!

    좋은 웹페이지 즐겨찾기