都知道在MVC5中,在action方法前加入 [ValidateAntiForgeryToken],会验证是否来自于自己表单的用户,验证其cook的token和来自表单中的token,是否一致
为方便更好的调试,直接调用其验证方法 AntiForgery.Validate();
// [AcceptVerbs(HttpVerbs.Post)] netCore没有这个
[HttpPost]
// [ValidateAntiForgeryToken]
public JsonResult Index(int a=1/*IFormCollection collection*/)
{
HttpCookie antiForgeryCookie = Request.Cookies[AntiForgeryConfig.CookieName];
string cookieValue = antiForgeryCookie != null ? antiForgeryCookie.Value : null;
AntiForgery.Validate(cookieValue, Request["__RequestVerificationToken"]);//Validate,就是框架源码验证的核心
ModelState.AddModelError("", "1111111111111");
return Json("验证成功!");
}
打开Validate方法
/// <summary>验证 HTML 表单字段中的输入数据是否来自已提交数据的用户。</summary>
/// <param name="cookieToken">Cookie 令牌值。</param>
/// <param name="formToken">令牌格式。</param>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public static void Validate(string cookieToken, string formToken)
{
if (HttpContext.Current == null)
throw new ArgumentException(WebPageResources.HttpContextUnavailable);
AntiForgery._worker.Validate((HttpContextBase) new HttpContextWrapper(HttpContext.Current), cookieToken, formToken);
}
打开 AntiForgery._worker.Validate方法
public void Validate(HttpContextBase httpContext, string cookieToken, string formToken)
{
this.CheckSSLConfig(httpContext);
AntiForgeryToken cookieToken1 = this.DeserializeToken(cookieToken);//来自Cookie的token
AntiForgeryToken formToken1 = this.DeserializeToken(formToken);//来自form表单的token
this._validator.ValidateTokens(httpContext, AntiForgeryWorker.ExtractIdentity(httpContext), cookieToken1, formToken1);
}
继续打开 this._validator.ValidateTokens(),方法
internal interface ITokenValidator
{
AntiForgeryToken GenerateCookieToken();
AntiForgeryToken GenerateFormToken(HttpContextBase httpContext,IIdentity identity,AntiForgeryToken cookieToken);
bool IsCookieTokenValid(AntiForgeryToken cookieToken);
void ValidateTokens(HttpContextBase httpContext,IIdentity identity,AntiForgeryToken cookieToken,AntiForgeryToken formToken);
}
由于 ValidateTokens 是需要实现的,找到实现方法,TokenValidator类的ValidateTokens,就是具体实现
public void ValidateTokens( HttpContextBase httpContext, IIdentity identity, AntiForgeryToken sessionToken, AntiForgeryToken fieldToken)
{
if (sessionToken == null)//1.来自cookies的token=null,直接验证失败
throw HttpAntiForgeryException.CreateCookieMissingException(this._config.CookieName);
if (fieldToken == null) //2.来自表单的token=null,直接验证失败
throw HttpAntiForgeryException.CreateFormFieldMissingException(this._config.FormFieldName);
if (!sessionToken.IsSessionToken || fieldToken.IsSessionToken) // 3.验证cookietoken的IsSessionToken 是否
throw HttpAntiForgeryException.CreateTokensSwappedException(this._config.CookieName, this._config.FormFieldName);
if (!object.Equals((object) sessionToken.SecurityToken, (object) fieldToken.SecurityToken))
throw HttpAntiForgeryException.CreateSecurityTokenMismatchException();
string str = string.Empty;
BinaryBlob binaryBlob = (BinaryBlob) null;
if (identity != null && identity.IsAuthenticated)
{
binaryBlob = this._claimUidExtractor.ExtractClaimUid(identity);
if (binaryBlob == null)
str = identity.Name ?? string.Empty;
}
bool flag = str.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || str.StartsWith("https://", StringComparison.OrdinalIgnoreCase);
if (!string.Equals(fieldToken.Username, str, flag ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase))
throw HttpAntiForgeryException.CreateUsernameMismatchException(fieldToken.Username, str);
if (!object.Equals((object) fieldToken.ClaimUid, (object) binaryBlob))
throw HttpAntiForgeryException.CreateClaimUidMismatchException();
if (this._config.AdditionalDataProvider != null && !this._config.AdditionalDataProvider.ValidateAdditionalData(httpContext, fieldToken.AdditionalData))
throw HttpAntiForgeryException.CreateAdditionalDataCheckFailedException();
}
透过这个方法,可以看出 这是个完全验证式的方法,此方法就是令牌验证最为关键的逻辑代码
验证的核心,在于两个token(两个AntiForgeryToken的对象),一个是cooktoken,一个formtoken,验证各自的属性是否一致,IsSessionToken为true表示Cookie令牌,否则为表单令牌
1.在开启了防伪标记功能后,FormToken或CookieToken其中一个值为空白!也就是说只要缺少表单令牌或者Cookie令牌,验证一定是失败的。
2.防伪令牌中安全令牌的值不相等!
3.防伪令牌中的相关授权信息不一致!例如出现认证授权的用户名不一致!
4.防伪令牌自身的标记错误,IsSessionToken为true表示Cookie令牌,否则为表单令牌,如果这个属性设置错误,验证失败!
5.其他的还会判断AdditionalDataProvider这个属性值是否
友情链接:https://shiyousan.com/post/636402934261643641
可以发现,验证来验证去,核心还是在于 AntiForgeryToken 这个密封类,
internal sealed class AntiForgeryToken
{
internal const int SecurityTokenBitLength = 128;
internal const int ClaimUidBitLength = 256;
private string _additionalData;
private BinaryBlob _securityToken;
private string _username;
public string AdditionalData
{
get
{
return this._additionalData ?? string.Empty;
}
set
{
this._additionalData = value;
}
}
public BinaryBlob ClaimUid { get; set; }
//true表示Cookie令牌,否则为表单令牌
public bool IsSessionToken { get; set; }
// 安全令牌
public BinaryBlob SecurityToken
{
get
{
if (this._securityToken == null)
this._securityToken = new BinaryBlob(128);
return this._securityToken;
}
set
{
this._securityToken = value;
}
}
public string Username
{
get
{
return this._username ?? string.Empty;
}
set
{
this._username = value;
}
}
}