都知道在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; } } }