@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 ...
随机推荐
- Linux Shell文件差集
file1-file2 => file3file1=/data/aaafile2=/data/bbbfile3=/data/cccsort -m <(sort $file1 | uniq) ...
- java自动生成表单简单实例
数据库表设置 tb_form(form表单) 字段 类型 约束 说明 Id Int 主键 主键 Formid Varchar2(20) 唯一 Form表单id的值 Action Varchar2(20 ...
- JAVA NIO之浅谈内存映射文件原理与DirectMemory
JAVA类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...
- C#操作计划任务
昨天有一个任务,就是要下载相关文件,然后保存在相关路径下,这个没什么难度,所以就略过不谈,主要谈谈定时下载,即每天某个固定时间执行下载,这个功能我是用C#代码来操作windows自带的任务计划来实现的 ...
- Selenium-一组元素的定位
一组元素的定位: 有时候我们可能需要定位一组元素,比如一组checkbox,这时候要实现的思路大概为: 先把一组元素识别出来,然后定位你需要的元素 下面是查找多个元素(这些方法将返回一个列表): 方法 ...
- 关于MFC消息的总结
一.MFC的消息类型 MFC的消息类型大致可以分为三种: 1.命令消息.由菜单和工具栏或快捷键产生,以WM_COMMAND形式发出(以WM_COMMAND发出的还有很多控件,如Button等,但它们产 ...
- codeforces 86D D. Powerful array(莫队算法)
题目链接: D. Powerful array time limit per test 5 seconds memory limit per test 256 megabytes input stan ...
- Android基于socket的群聊程序
在网上看了好多,但是感觉不是太简单就是只能单独聊,所以就自己写了个可以群聊的,直接上代码了 一.服务器端 这里用的MyEclipse作为服务器端 MyServerScoket.java package ...
- python 标准库 —— http(http.cookiejar)
1. cookie 信息的读取 from urllib import request import http from http import cookiejar cookie = cookiejar ...
- TypeError: 'str' object is not callable
Python报错TypeError: 'str' object is not callable