通过 AntiForgeryWorker的GetHtml()方法生成html --input hide元素--value=要验证的值,并生成cookie--用于保存需要验证的值。

类中的AntiForgeryDataSerializer--用作序列化与反序列化验证的值。

internal class AntiForgeryWorker {
public AntiForgeryWorker() {
Serializer = new AntiForgeryDataSerializer();
} internal AntiForgeryDataSerializer Serializer {
get;
set;
} private static HttpAntiForgeryException CreateValidationException() {
return new HttpAntiForgeryException(WebPageResources.AntiForgeryToken_ValidationFailed);
} public HtmlString GetHtml(HttpContextBase httpContext, string salt, string domain, string path) {
Debug.Assert(httpContext != null); string formValue = GetAntiForgeryTokenAndSetCookie(httpContext, salt, domain, path);
string fieldName = AntiForgeryData.GetAntiForgeryTokenName(null); TagBuilder builder = new TagBuilder("input");
builder.Attributes["type"] = "hidden";
builder.Attributes["name"] = fieldName;
builder.Attributes["value"] = formValue;
return new HtmlString(builder.ToString(TagRenderMode.SelfClosing));
} private string GetAntiForgeryTokenAndSetCookie(HttpContextBase httpContext, string salt, string domain, string path) {
string cookieName = AntiForgeryData.GetAntiForgeryTokenName(httpContext.Request.ApplicationPath); AntiForgeryData cookieToken = null;
HttpCookie cookie = httpContext.Request.Cookies[cookieName];
if (cookie != null) {
try {
cookieToken = Serializer.Deserialize(cookie.Value);
}
catch (HttpAntiForgeryException) { }
} if (cookieToken == null) {
cookieToken = AntiForgeryData.NewToken();
string cookieValue = Serializer.Serialize(cookieToken); HttpCookie newCookie = new HttpCookie(cookieName, cookieValue) { HttpOnly = true, Domain = domain };
if (!String.IsNullOrEmpty(path)) {
newCookie.Path = path;
}
httpContext.Response.Cookies.Set(newCookie);
} AntiForgeryData formToken = new AntiForgeryData(cookieToken) {
Salt = salt,
Username = AntiForgeryData.GetUsername(httpContext.User)
};
return Serializer.Serialize(formToken);
} public void Validate(HttpContextBase context, string salt) {
Debug.Assert(context != null); string fieldName = AntiForgeryData.GetAntiForgeryTokenName(null);
string cookieName = AntiForgeryData.GetAntiForgeryTokenName(context.Request.ApplicationPath); HttpCookie cookie = context.Request.Cookies[cookieName];
if (cookie == null || String.IsNullOrEmpty(cookie.Value)) {
// error: cookie token is missing
throw CreateValidationException();
}
AntiForgeryData cookieToken = Serializer.Deserialize(cookie.Value); string formValue = context.Request.Form[fieldName];
if (String.IsNullOrEmpty(formValue)) {
// error: form token is missing
throw CreateValidationException();
}
AntiForgeryData formToken = Serializer.Deserialize(formValue); if (!String.Equals(cookieToken.Value, formToken.Value, StringComparison.Ordinal)) {
// error: form token does not match cookie token
throw CreateValidationException();
} string currentUsername = AntiForgeryData.GetUsername(context.User);
if (!String.Equals(formToken.Username, currentUsername, StringComparison.OrdinalIgnoreCase)) {
// error: form token is not valid for this user
// (don't care about cookie token)
throw CreateValidationException();
} if (!String.Equals(salt ?? String.Empty, formToken.Salt, StringComparison.Ordinal)) {
// error: custom validation failed
throw CreateValidationException();
}
}
}
internal class AntiForgeryDataSerializer {
[SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times",
Justification = "MemoryStream is resilient to double-Dispose")]
public virtual AntiForgeryData Deserialize(string serializedToken) {
if (String.IsNullOrEmpty(serializedToken)) {
throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "serializedToken");
} try {
using (MemoryStream stream = new MemoryStream(Decoder(serializedToken)))
using (BinaryReader reader = new BinaryReader(stream)) {
return new AntiForgeryData {
Salt = reader.ReadString(),
Value = reader.ReadString(),
CreationDate = new DateTime(reader.ReadInt64()),
Username = reader.ReadString()
};
}
}
catch (Exception ex) {
throw new HttpAntiForgeryException(WebPageResources.AntiForgeryToken_ValidationFailed, ex);
}
} [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times",
Justification = "MemoryStream is resilient to double-Dispose")]
public virtual string Serialize(AntiForgeryData token) {
if (token == null) {
throw new ArgumentNullException("token");
} using (MemoryStream stream = new MemoryStream())
using (BinaryWriter writer = new BinaryWriter(stream)) {
writer.Write(token.Salt);
writer.Write(token.Value);
writer.Write(token.CreationDate.Ticks);
writer.Write(token.Username); return Encoder(stream.ToArray());
}
} // Testing hooks internal Func<string, byte[]> Decoder =
(value) => MachineKey.Decode(Base64ToHex(value), MachineKeyProtection.All); internal Func<byte[], string> Encoder =
(bytes) => HexToBase64(MachineKey.Encode(bytes, MachineKeyProtection.All).ToUpperInvariant()); // String transformation helpers private static string Base64ToHex(string base64) {
StringBuilder builder = new StringBuilder(base64.Length * );
foreach (byte b in Convert.FromBase64String(base64)) {
builder.Append(HexDigit(b >> ));
builder.Append(HexDigit(b & 0x0F));
}
string result = builder.ToString();
return result;
} private static char HexDigit(int value) {
return (char)(value > ? value + '' : value + '');
} private static int HexValue(char digit) {
return digit > '' ? digit - '' : digit - '';
} private static string HexToBase64(string hex) {
int size = hex.Length / ;
byte[] bytes = new byte[size];
for (int idx = ; idx < size; idx++) {
bytes[idx] = (byte)((HexValue(hex[idx * ]) << ) + HexValue(hex[idx * + ]));
}
string result = Convert.ToBase64String(bytes);
return result;
}
}

AntiForgeryData--验证保存的类,即值,使用RNGCryptoServiceProvider加密。

internal sealed class AntiForgeryData {

        private const string AntiForgeryTokenFieldName = "__RequestVerificationToken";

        private const int TokenLength =  / ;
private readonly static RNGCryptoServiceProvider _prng = new RNGCryptoServiceProvider(); private DateTime _creationDate = DateTime.UtcNow;
private string _salt;
private string _username;
private string _value; public AntiForgeryData() {
} // copy constructor
public AntiForgeryData(AntiForgeryData token) {
if (token == null) {
throw new ArgumentNullException("token");
} CreationDate = token.CreationDate;
Salt = token.Salt;
Username = token.Username;
Value = token.Value;
} public DateTime CreationDate {
get {
return _creationDate;
}
set {
_creationDate = value;
}
} public string Salt {
get {
return _salt ?? String.Empty;
}
set {
_salt = value;
}
} public string Username {
get {
return _username ?? String.Empty;
}
set {
_username = value;
}
} public string Value {
get {
return _value ?? String.Empty;
}
set {
_value = value;
}
} private static string Base64EncodeForCookieName(string s) {
byte[] rawBytes = Encoding.UTF8.GetBytes(s);
string base64String = Convert.ToBase64String(rawBytes); // replace base64-specific characters with characters that are safe for a cookie name
return base64String.Replace('+', '.').Replace('/', '-').Replace('=', '_');
} private static string GenerateRandomTokenString() {
byte[] tokenBytes = new byte[TokenLength];
_prng.GetBytes(tokenBytes); string token = Convert.ToBase64String(tokenBytes);
return token;
} // If the app path is provided, we're generating a cookie name rather than a field name, and the cookie names should
// be unique so that a development server cookie and an IIS cookie - both running on localhost - don't stomp on
// each other.
internal static string GetAntiForgeryTokenName(string appPath) {
if (String.IsNullOrEmpty(appPath)) {
return AntiForgeryTokenFieldName;
}
else {
return AntiForgeryTokenFieldName + "_" + Base64EncodeForCookieName(appPath);
}
} internal static string GetUsername(IPrincipal user) {
if (user != null) {
IIdentity identity = user.Identity;
if (identity != null && identity.IsAuthenticated) {
return identity.Name;
}
} return String.Empty;
} public static AntiForgeryData NewToken() {
string tokenString = GenerateRandomTokenString();
return new AntiForgeryData() {
Value = tokenString
};
} }

AntiForgery--用于对以上功能的公开类

public static class AntiForgery {
private static readonly AntiForgeryWorker _worker = new AntiForgeryWorker(); public static HtmlString GetHtml() {
if (HttpContext.Current == null) {
throw new ArgumentException(WebPageResources.HttpContextUnavailable);
} return GetHtml(new HttpContextWrapper(HttpContext.Current), salt: null, domain: null, path: null);
} public static HtmlString GetHtml(HttpContextBase httpContext, string salt, string domain, string path) {
if (httpContext == null) {
throw new ArgumentNullException("httpContext");
} return _worker.GetHtml(httpContext, salt, domain, path);
} public static void Validate() {
if (HttpContext.Current == null) {
throw new ArgumentException(WebPageResources.HttpContextUnavailable);
}
Validate(new HttpContextWrapper(HttpContext.Current), salt: null);
} public static void Validate(HttpContextBase httpContext, string salt) {
if (httpContext == null) {
throw new ArgumentNullException("httpContext");
} _worker.Validate(httpContext, salt);
}
}

使用ValidateAntiForgeryTokenAttribute特性,用于在要验证的方法上,其中默认是采用上面AntiForgery.Validate来验证。

也可以传自己的--salt生成验证的票据。

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class ValidateAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter { private string _salt; public string Salt {
get {
return _salt ?? String.Empty;
}
set {
_salt = value;
}
} internal Action<HttpContextBase, string> ValidateAction {
get;
private set;
} public ValidateAntiForgeryTokenAttribute()
: this(AntiForgery.Validate) {
} internal ValidateAntiForgeryTokenAttribute(Action<HttpContextBase,string> validateAction) {
Debug.Assert(validateAction != null);
ValidateAction = validateAction;
} public void OnAuthorization(AuthorizationContext filterContext) {
if (filterContext == null) {
throw new ArgumentNullException("filterContext");
} ValidateAction(filterContext.HttpContext, Salt);
}
}

使用方式

1.在view里的form中生成验证值

@using (Html.BeginForm()) {
@Html.AntiForgeryToken()

2.在对应的action中

       [HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOn(LogOnModel model, string returnUrl)

从以上可分析可知,存值cookie和获取值(从form中获取)都是可以在负载中有效。

所以放心在负载中使用,增加你的程序的安全性。

mvc中AntiForgeryToken的实现方式--看Mvc源码的更多相关文章

  1. asp.net开发中常见公共捕获异常方式总结(附源码)

    本文实例总结了asp.net开发中常见公共捕获异常方式.分享给大家供大家参考,具体如下: 前言:在实际开发过程中,对于一个应用系统来说,应该有自己的一套成熟的异常处理框架,这样当异常发生时,也能得到统 ...

  2. 一点一点看JDK源码(〇)

    一点一点看JDK源码(〇) liuyuhang原创,未经允许进制转载 写在前面: 几乎所有的大神都会强调看源码,也强调源码的重要性: 但是如何看源码,源码看什么?看了什么用?看了怎么用? 困扰很多人, ...

  3. 一点一点看JDK源码(一)Collection体系概览

    一点一点看JDK源码(一)Collection体系概览 liuyuhang原创,未经允许进制转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.综述 Collection为集 ...

  4. 一点一点看JDK源码(二)java.util.List

    一点一点看JDK源码(二)java.util.List liuyuhang原创,未经允许进制转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.综述 List译为表,一览表, ...

  5. 一点一点看JDK源码(四)java.util.ArrayList 中篇

    一点一点看JDK源码(四)java.util.ArrayList 中篇 liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.综述 在前篇中 ...

  6. 一点一点看JDK源码(五)java.util.ArrayList 后篇之sort与Comparator

    一点一点看JDK源码(五)java.util.ArrayList 后篇之sort与Comparator liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JD ...

  7. 一点一点看JDK源码(六)java.util.LinkedList前篇之链表概要

    一点一点看JDK源码(六)java.util.LinkedList前篇之链表概要 liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.什么 ...

  8. 零基础带你看Spring源码——IOC控制反转

    本章开始来学习下Spring的源码,看看Spring框架最核心.最常用的功能是怎么实现的. 网上介绍Spring,说源码的文章,大多数都是生搬硬推,都是直接看来的观点换个描述就放出来.这并不能说有问题 ...

  9. 带着问题看redux源码

    前言 作为前端状态管理器,这个比较跨时代的工具库redux有很多实现和思想值得我们思考.在深入源码之前,我们可以相关注下一些常见问题,这样带着问题去看实现,也能更加清晰的了解. 常见问题 大概看了下主 ...

随机推荐

  1. WPF多屏最大化

    如果计算机存在多个显示器,这时设置wpf窗口为最大化,窗口只能在主显示器中实现最大化,如果想要实现窗口拉伸至多屏,需要获取所有显示器分辨率之和.这时用到了System.Windows.SystemPa ...

  2. 一起学Hadoop——实现两张表之间的连接操作

    ---恢复内容开始--- 之前我们都是学习使用MapReduce处理一张表的数据(一个文件可视为一张表,hive和关系型数据库Mysql.Oracle等都是将数据存储在文件中).但是我们经常会遇到处理 ...

  3. Bash远程代码执行漏洞(CVE-2014-6271)案例分析

    Web服务器和CGI的关系 什么是WEB服务器(IIS.Nginx.Apache) WEB服务器也称为WWW(WORLD WIDE WEB)服务器,主要功能是提供网上信息浏览服务.(1)应用层使用HT ...

  4. 今天开始学习php,粘一些重点放这以便查看记忆。

    1.PHP的变量用$定义. PHP 将所有全局变量存储在一个名为 $GLOBALS[index] 的数组中. index 保存变量的名称.这个数组可以在函数内部访问,也可以直接用来更新全局变量. 2. ...

  5. 059 SparkStream介绍

    离线计算框架:MR,hive-->对时间要求不严格 实时计算框架:SparkCore-->要求job执行时间比较快 交互式计算框架:SparkSQL,Hive,-->提供SQL操作的 ...

  6. mysql 分区 1526错误

    mysql 分区 原文:http://fyzjhh.blog.163.com/blog/static/1694442262012544429953/ 参考:https://bugs.mysql.com ...

  7. 更新pip和setuptools

    python -m pip install -U pip setuptools

  8. 四、Python导入自己写的包报错:没有该包如何解决

    场景:当你运行文件时,提示没有你想要导入的这个包,这是只需在你运行文件的开头添加俩行代码: import sys sys.path.apprnd('../') 原理:请看图解 另外解释下sys.pat ...

  9. hdu3038

    hdu3038带权并查集这种问题不仅仅要处理不同的点的是否在同一个集合里之类的问题,点与点之间存在连线,其带有权值,在路径压缩的时候也要对权值进行操作这道题就是带权并查集+向量去做 #include& ...

  10. 分布式事务?咱先弄明白本地事务再说 - ACID

      过去一段时间面试的同学,对于数据库事务,可以按照配置正常使用,但很多都无法讲清楚和理解数据库事务这个东西真正的意义,以及互联网兴起以后,当今数据库在ACID面前面临怎样的问题和抉择. 事务,是各大 ...