源碼來自MVC4
@Html.AntiForgeryToken() 源碼分析
public MvcHtmlString AntiForgeryToken() { return new MvcHtmlString(AntiForgery.GetHtml().ToString()); }
AntiForgery源自System.Web.Helpers.AntiForgery
public static HtmlString GetHtml() { if (HttpContext.Current == null) { throw new ArgumentException(WebPageResources.HttpContextUnavailable); } TagBuilder formInputElement = AntiForgery._worker.GetFormInputElement(new HttpContextWrapper(HttpContext.Current)); return formInputElement.ToHtmlString(TagRenderMode.SelfClosing); }
//查到_worker的創建
private static readonly AntiForgeryWorker _worker = AntiForgery.CreateSingletonAntiForgeryWorker();
//發現IAntiForgeryTokenSerializer來自AntiForgeryTokenSerializer
//而且發現用所以接口的實例對象,開始查看具體代碼實現
private static AntiForgeryWorker CreateSingletonAntiForgeryWorker() { ICryptoSystem cryptoSystem = MachineKey45CryptoSystem.Instance; if (cryptoSystem == null) { cryptoSystem = new MachineKey40CryptoSystem(); } IAntiForgeryConfig config = new AntiForgeryConfigWrapper(); IAntiForgeryTokenSerializer serializer = new AntiForgeryTokenSerializer(cryptoSystem); ITokenStore tokenStore = new AntiForgeryTokenStore(config, serializer); IClaimUidExtractor claimUidExtractor = new ClaimUidExtractor(config, ClaimsIdentityConverter.Default); ITokenValidator validator = new TokenValidator(config, claimUidExtractor); return new AntiForgeryWorker(serializer, config, tokenStore, validator); }
//_worker的GetFormInputElement,發現value是_serializer.Serialize序列出來的,於是查看AntiForgeryTokenSerializer對象
public TagBuilder GetFormInputElement(HttpContextBase httpContext) { this.CheckSSLConfig(httpContext); AntiForgeryToken cookieTokenNoThrow = this.GetCookieTokenNoThrow(httpContext); AntiForgeryToken antiForgeryToken; AntiForgeryToken token; this.GetTokens(httpContext, cookieTokenNoThrow, out antiForgeryToken, out token); if (antiForgeryToken != null) { this._tokenStore.SaveCookieToken(httpContext, antiForgeryToken); } TagBuilder tagBuilder = new TagBuilder("input"); tagBuilder.Attributes["type"] = "hidden"; tagBuilder.Attributes["name"] = this._config.FormFieldName; tagBuilder.Attributes["value"] = this._serializer.Serialize(token); return tagBuilder; }
//查到AntiForgeryTokenSerializer對象的Serialize函數
public string Serialize(AntiForgeryToken token) { string result; using (MemoryStream memoryStream = new MemoryStream()) { using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream)) { binaryWriter.Write(1); binaryWriter.Write(token.SecurityToken.GetData()); binaryWriter.Write(token.IsSessionToken); if (!token.IsSessionToken) { if (token.ClaimUid != null) { binaryWriter.Write(true); binaryWriter.Write(token.ClaimUid.GetData()); } else { binaryWriter.Write(false); binaryWriter.Write(token.Username); } binaryWriter.Write(token.AdditionalData); } binaryWriter.Flush(); result = this._cryptoSystem.Protect(memoryStream.ToArray()); } } return result; }
/關鍵的序列化函數
//產生疑惑token.SecurityToken.GetData(),這個數據哪里來的
//token.IsSessionToken這個是bool,看命名就知道是用來判斷是不是生成關聯session的token
//退回后查看AntiForgeryToken這個class
//
//發現這段 GetFormInputElement函數里面如此創建
AntiForgeryToken token;
this.GetTokens(httpContext, cookieTokenNoThrow, out antiForgeryToken, out token);
//通過上方查看_worker知道的實例
// ITokenStore tokenStore = new AntiForgeryTokenStore(config, serializer);
//查到
private void GetTokens(HttpContextBase httpContext, AntiForgeryToken oldCookieToken, out AntiForgeryToken newCookieToken, out AntiForgeryToken formToken) { newCookieToken = null; if (!this._validator.IsCookieTokenValid(oldCookieToken)) { AntiForgeryToken antiForgeryToken; newCookieToken = (antiForgeryToken = this._validator.GenerateCookieToken()); oldCookieToken = antiForgeryToken; } formToken = this._validator.GenerateFormToken(httpContext, AntiForgeryWorker.ExtractIdentity(httpContext), oldCookieToken); }
//繼續追看
public AntiForgeryToken GenerateFormToken(HttpContextBase httpContext, IIdentity identity, AntiForgeryToken cookieToken) { AntiForgeryToken antiForgeryToken = new AntiForgeryToken { SecurityToken = cookieToken.SecurityToken, IsSessionToken = false//原來默認是false,暫時認為默認是不使用session的 }; bool flag = false; if (identity != null && identity.IsAuthenticated) { if (!this._config.SuppressIdentityHeuristicChecks) { flag = true; } antiForgeryToken.ClaimUid = this._claimUidExtractor.ExtractClaimUid(identity); if (antiForgeryToken.ClaimUid == null) { antiForgeryToken.Username = identity.Name; } } if (this._config.AdditionalDataProvider != null) { antiForgeryToken.AdditionalData = this._config.AdditionalDataProvider.GetAdditionalData(httpContext); } if (flag && string.IsNullOrEmpty(antiForgeryToken.Username) && antiForgeryToken.ClaimUid == null && string.IsNullOrEmpty(antiForgeryToken.AdditionalData)) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, WebPageResources.TokenValidator_AuthenticatedUserWithoutUsername, new object[] { identity.GetType() })); } return antiForgeryToken; }
//經過這樣一看token的創建就清晰了
//接着查看token的token.SecurityToken這個屬性
public BinaryBlob SecurityToken { get { if (this._securityToken == null) { this._securityToken = new BinaryBlob(128);//發現默認是128,對象是BinaryBlob } return this._securityToken; } set { this._securityToken = value; } }
//查看BinaryBlob的構造函數,出現一個GenerateNewToken函數
public BinaryBlob(int bitLength) : this(bitLength, BinaryBlob.GenerateNewToken(bitLength)) { }
//GenerateNewToken源碼
private static byte[] GenerateNewToken(int bitLength) { byte[] array = new byte[bitLength / 8]; BinaryBlob._prng.GetBytes(array); return array; }
//該死,有出現一個未知的東西,_prng
private static readonly RNGCryptoServiceProvider _prng = new RNGCryptoServiceProvider();
[SecuritySafeCritical] public override void GetBytes(byte[] data) { if (data == null) { throw new ArgumentNullException("data"); } RNGCryptoServiceProvider.GetBytes(this.m_safeProvHandle, data, data.Length); } [SecurityCritical, SuppressUnmanagedCodeSecurity] [DllImport("QCall", CharSet = CharSet.Unicode)] private static extern void GetBytes(SafeProvHandle hProv, byte[] randomBytes, int count);
//至此必應了一下RNGCryptoServiceProvider類(bing查msdn特別方便)
//https://msdn.microsoft.com/zh-cn/library/system.security.cryptography.rngcryptoserviceprovider(v=vs.110).aspx
//http://www.cnblogs.com/izanami/archive/2011/04/20/2022173.html
原來RNGCryptoServiceProvider的GetBytes用經過加密的強隨機值序列填充字節數組,最終的隨機數據生成!
//現在這段序列化的部分已經解開了一些了 binaryWriter.Write(1); binaryWriter.Write(token.SecurityToken.GetData());//數據明了,一段用經過加密的強隨機值數組 binaryWriter.Write(token.IsSessionToken);//Bool判斷是否使用session if (!token.IsSessionToken) { if (token.ClaimUid != null) { binaryWriter.Write(true); binaryWriter.Write(token.ClaimUid.GetData());//也是一個BinaryBlob } else { binaryWriter.Write(false); binaryWriter.Write(token.Username); } binaryWriter.Write(token.AdditionalData); } binaryWriter.Flush(); result = this._cryptoSystem.Protect(memoryStream.ToArray());
//繼續解開ClaimUid
//在上文的 GenerateFormToken發現這樣的一段
antiForgeryToken.ClaimUid = this._claimUidExtractor.ExtractClaimUid(identity);
//_claimUidExtractor在創建的地方是ClaimUidExtractor
//發現源碼
public BinaryBlob ExtractClaimUid(IIdentity identity) { if (identity == null || !identity.IsAuthenticated || this._config.SuppressIdentityHeuristicChecks) { return null; } ClaimsIdentity claimsIdentity = this._claimsIdentityConverter.TryConvert(identity); if (claimsIdentity == null) { return null; } string[] uniqueIdentifierParameters = ClaimUidExtractor.GetUniqueIdentifierParameters(claimsIdentity, this._config.UniqueClaimTypeIdentifier); byte[] data = CryptoUtil.ComputeSHA256(uniqueIdentifierParameters); return new BinaryBlob(256, data); }
public static byte[] ComputeSHA256(IList<string> parameters) { byte[] result; using (MemoryStream memoryStream = new MemoryStream()) { using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream)) { foreach (string current in parameters) { binaryWriter.Write(current); } binaryWriter.Flush(); using (SHA256 sHA = CryptoUtil._sha256Factory()) { byte[] array = sHA.ComputeHash(memoryStream.GetBuffer(), 0, checked((int)memoryStream.Length)); result = array; } } } return result; }
//發現是SHA256的哈希計算值
//然后是AdditionalData
public string AdditionalData { get { return this._additionalData ?? string.Empty; } set { this._additionalData = value; } }
//最后是這句result = this._cryptoSystem.Protect(memoryStream.ToArray());
//MachineKey40CryptoSystem : ICryptoSystem
public string Protect(byte[] data) { byte[] array = new byte[data.Length + 4]; Buffer.BlockCopy(data, 0, array, 4, data.Length); array[0] = 133; array[1] = 135; array[2] = 242; array[3] = 102; string hex = this._encoder(array, MachineKeyProtection.All); return MachineKey40CryptoSystem.HexToBase64(hex); } internal static string HexToBase64(string hex) { int num = hex.Length / 2; byte[] array = new byte[num]; for (int i = 0; i < num; i++) { array[i] = (byte)((MachineKey40CryptoSystem.HexValue(hex[i * 2]) << 4) + MachineKey40CryptoSystem.HexValue(hex[i * 2 + 1])); } return HttpServerUtility.UrlTokenEncode(array); }
//將那些隨機生成的數據變成了16進制字符串
//加密到此結束了
//最后就是對應的解密了
public AntiForgeryToken Deserialize(string serializedToken) { try { using (MemoryStream memoryStream = new MemoryStream(this._cryptoSystem.Unprotect(serializedToken))) { using (BinaryReader binaryReader = new BinaryReader(memoryStream)) { AntiForgeryToken antiForgeryToken = AntiForgeryTokenSerializer.DeserializeImpl(binaryReader); if (antiForgeryToken != null) { return antiForgeryToken; } } } } catch { } throw HttpAntiForgeryException.CreateDeserializationFailedException(); } private static AntiForgeryToken DeserializeImpl(BinaryReader reader) { byte b = reader.ReadByte();//從當前流中讀取下一個字節,並使流的當前位置提升 1 個字節。 if (b != 1)//對應加密的binaryWriter.Write(1); { return null; } //依照加密時候的分段大小對應解密 AntiForgeryToken antiForgeryToken = new AntiForgeryToken(); byte[] data = reader.ReadBytes(16); antiForgeryToken.SecurityToken = new BinaryBlob(128, data); antiForgeryToken.IsSessionToken = reader.ReadBoolean(); if (!antiForgeryToken.IsSessionToken) { bool flag = reader.ReadBoolean(); if (flag) { byte[] data2 = reader.ReadBytes(32); antiForgeryToken.ClaimUid = new BinaryBlob(256, data2); } else { antiForgeryToken.Username = reader.ReadString(); } antiForgeryToken.AdditionalData = reader.ReadString(); } if (reader.BaseStream.ReadByte() != -1) { return null; } return antiForgeryToken; }
