@Html.AntiForgeryToken() 源码分析,表单防伪码的生成
源码来自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();
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,对象是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 / ];
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();
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(, 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(), , 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 + ];
Buffer.BlockCopy(data, , array, , data.Length);
array[] = ;
array[] = ;
array[] = ;
array[] = ;
string hex = this._encoder(array, MachineKeyProtection.All);
return MachineKey40CryptoSystem.HexToBase64(hex);
} internal static string HexToBase64(string hex)
{
int num = hex.Length / ;
byte[] array = new byte[num];
for (int i = ; i < num; i++)
{
array[i] = (byte)((MachineKey40CryptoSystem.HexValue(hex[i * ]) << ) + MachineKey40CryptoSystem.HexValue(hex[i * + ]));
}
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 != )//对应加密的binaryWriter.Write(1);
{
return null;
}
//依照加密时候的分段大小对应解密
AntiForgeryToken antiForgeryToken = new AntiForgeryToken();
byte[] data = reader.ReadBytes();
antiForgeryToken.SecurityToken = new BinaryBlob(, data);
antiForgeryToken.IsSessionToken = reader.ReadBoolean();
if (!antiForgeryToken.IsSessionToken)
{
bool flag = reader.ReadBoolean();
if (flag)
{
byte[] data2 = reader.ReadBytes();
antiForgeryToken.ClaimUid = new BinaryBlob(, data2);
}
else
{
antiForgeryToken.Username = reader.ReadString();
}
antiForgeryToken.AdditionalData = reader.ReadString();
}
if (reader.BaseStream.ReadByte() != -)
{
return null;
}
return antiForgeryToken;
}
@Html.AntiForgeryToken() 源码分析,表单防伪码的生成的更多相关文章
- Spring IOC 容器源码分析 - 创建单例 bean 的过程
1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...
- Spring IOC 容器源码分析 - 获取单例 bean
1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...
- spark 源码分析之十九 -- DAG的生成和Stage的划分
上篇文章 spark 源码分析之十八 -- Spark存储体系剖析 重点剖析了 Spark的存储体系.从本篇文章开始,剖析Spark作业的调度和计算体系. 在说DAG之前,先简单说一下RDD. 对RD ...
- dubbo源码分析三:consumer注册及生成代理对象
本章我们将分析一下consumer向注册中心注册,并获取服务端相应的信息,根据这些信息生产代理对象的过程和源码. 1.类图 上图展示了部分消费者注册及生成代理对象过程中需要使用到的类和接口,其中: s ...
- cglib源码分析(三):Class生成策略
cglib中生成类的工作是由AbstractClassGenerator的create方法使用相应的生成策略完成,具体代码如下: private GeneratorStrategy strategy ...
- [转]数据库中间件 MyCAT源码分析——跨库两表Join
1. 概述 2. 主流程 3. ShareJoin 3.1 JoinParser 3.2 ShareJoin.processSQL(...) 3.3 BatchSQLJob 3.4 ShareDBJo ...
- spark源码分析以及优化
第一章.spark源码分析之RDD四种依赖关系 一.RDD四种依赖关系 RDD四种依赖关系,分别是 ShuffleDependency.PrunDependency.RangeDependency和O ...
- Spring AOP 源码分析 - 拦截器链的执行过程
1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...
- Spring AOP 源码分析 - 创建代理对象
1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...
随机推荐
- maven 相关插件
maven打包配置,到底要打包哪些文件,如何配置??使用如下插件: <build> <finalName>weatherAdminSys</finalName> & ...
- 遇到“拒绝了对对象的 EXECUTE 权限”和“无法作为数据库主体执行,因为主体 "dbo" 不存在、无法模拟这种类型的主体,或您没有所需的权限”的问题
在将数据库从sqlserver2000迁移到2005后,原有的用户名TDS在执行存储过程是报错:“拒绝了对对象的 EXECUTE 权限”. 如网上所说,在使用的数据库的属性页->权限中给TDS添 ...
- linux rpm包的编译
有些软件包的特性是编译者选定的,如果编译未选定此特性,将无法使用.rpm包的版本落后于源码包. 因此需要定制安装,也就是手动编译安装. 编译需要编译环境. 编译的过程如下: 1.下载源码 2.执行 t ...
- 0X7FFFFFFF,0X80000000
给int类型赋值的话,0X7FFFFFFF代表最大值,0X80000000代表最小值 INT_MAX 代表最大值, INT_MIN 代表最小值 #include<iostream> #in ...
- 前向纠错码(FEC)的RTP荷载格式
http://www.rosoo.net/a/201110/15146.html 本文档规定了一般性的前向纠错的媒体数据流的RTP打包格式.这种格式针对基于异或操作的FEC算法进行了特殊设计,它允许终 ...
- Skype SILK vs. iLBC vs. Speex
对比一下这三种VOIP语音算法的特点: 1 参数与特征 2 SILK性能 关于iLBC和Speex的性能可以参考以前写的文章. 3 关于VOIP一些观点(仅代表个人观点) 1) Skype 辛苦三年 ...
- NYOJ-小猴子下落
描述 有一颗二叉树,最大深度为D,且所有叶子的深度都相同.所有结点从左到右从上到下的编号为1,2,3,·····,2的D次方减1.在结点1处放一个小猴子,它会往下跑.每个内结点上都有一个开关,初始全部 ...
- nodejs调试:node-inspector
基于Chrome浏览器的调试器 既然我们可以通过V8的调试插件来调试,那是否也可以借用Chrome浏览器的JavaScript调试器来调试呢?node-inspector模块提供了这样一种可能.我们需 ...
- [转]jQuery.extend 函数详解
JQuery的extend扩展方法: Jquery的扩展方法extend是我们在写插件的过程中常用的方法,该方法有一些重载原型,在此,我们一起去了解了解. 一.Jquery的扩展方 ...
- DOM基础知识整理
--<JavaScript高级程序设计>Chapter10学习笔记 一.节点层次 1.Node类型 1)(Node).nodeType属性 节点nodeType属性有12种. 检测node ...