@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 ps aux 结果解释
# ps aux | moreUSER PID %CPU %MEM VSZ RSS TTY STAT START ...
- JAVA强制类型转换(转载+自己的感想) - stemon
JAVA强制类型转换(转载+自己的感想) - stemon 时间 2013-10-29 15:52:00 博客园-Java原文 http://www.cnblogs.com/stemon/p/33 ...
- shell 中 exec 和 source 命令解析
文章大部分来源于:http://www.cnblogs.com/zhaoyl/archive/2012/07/07/2580749.html 作为笔记留存博客备忘. exec和source都属于bas ...
- SQLServer 一些有用的语句
SET STATISTICS TIME ON 记录查询的相关数据 生成随机Guid SELECT NewID() 按照某一列排序并生成序号 select Row_Number() OVER (ORDE ...
- 五 Django框架,models.py模块,数据库操作——表类容的增删改查
Django框架,models.py模块,数据库操作——表类容的增删改查 增加数据 create()方法,增加数据 save()方法,写入数据 第一种方式 表类名称(字段=值) 需要save()方法, ...
- python 特征选择 绘图 + mine
demo代码: # _*_coding:UTF-8_*_ import numpy as np import sys import pandas as pd from pandas import Se ...
- python UDP CS demo
UDP Communication Contents UDP Communication Sending Receiving Using UDP for e.g. File Transfers Mul ...
- Struts2 第一个入门小案例
1.加载类库 2 配置web.xml文件 3.开发视图层 4.开发控制层Action 5.配置struts.xml 6.部署运行
- 201621123014《JAVA程序设计》第2周学习总结
1. 本周学习总结 引用数据类型:JAVA定义字符串实际上是创建字符串的引用,将引用指向需要的字符串. 字符串常量池:直接对引用赋值时,会先在字符串中搜索是否有这个对象,已有则不创建直接指向它. St ...
- Statement
题目大意 给定一棵基环外向树,和若干组询问,对于每次独立的询问都指定一些起点和一些终点,你删去一些边,使得从任意起点出发都无法到达终点,并让删去的边的编号的最小值最大,求这个最大的最小值. 题解 不难 ...