码迷,mamicode.com
首页 > Web开发 > 详细

@Html.AntiForgeryToken() 源码分析,表单防伪码的生成

时间:2017-01-17 23:26:07      阅读:754      评论:0      收藏:0      [点我收藏+]

标签:stream   html   ssi   lib   set   err   arch   tcl   images   

源码来自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;
}

 

 

文章系转载 http://www.cnblogs.com/RainbowInTheSky/p/5565248.html

@Html.AntiForgeryToken() 源码分析,表单防伪码的生成

标签:stream   html   ssi   lib   set   err   arch   tcl   images   

原文地址:http://www.cnblogs.com/sunice/p/6294907.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!