通过 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. JS高级程序设计3

    PS:有一小部分写在了 JS 2017了 JSON <!DOCTYPE html> <html lang="en"> <head> <me ...

  2. Adjoint operators $T_K$ and $T_{K^{*}}$ in BEM

    In our last article, we introduced four integral operators in the boundary integral equations in BEM ...

  3. linux重启服务的脚本命令

    最近做网站测试,每次测试完成都要重启服务,为此写了一个简单的shell脚本 linux服务重启shell脚本示例 2014年12月18日 linux服务重启脚本,如何实现linux服务的定时重启,可以 ...

  4. windows server远程连接提示“终端服务器超出了最大允许连接”

  5. linux中通过lsof恢复删除的文件,前题是fd被占用。

    http://www.serverwatch.com/tutorials/article.php/3822816/Recovering-Deleted-Files-With-lsof.htm One ...

  6. Cloudera Manager的安装

    1.  cloudera manager的概念 简单来说,Cloudera Manager是一个拥有集群自动化安装.中心化管理.集群监控.报警功能的一个工具(软件),使得安装集群从几天的时间缩短在几个 ...

  7. union表关联模糊查询servlet,action方法

    2018-11-14 servletxml层 public String getSql(String keyword) { StringBuffer sqlSb = new StringBuffer( ...

  8. HDU 5178 pairs【二分】||【尺取】

    <题目链接> 题目大意: 给定一个整数序列,求出绝对值小于等于k的有序对个数. 解题分析: $O(nlong(n))$的二分很好写,这里就不解释了.本题尺取$O(n)$也能做,并且效率很不 ...

  9. NiftyNet开源平台使用

    NiftyNet是一款开源的卷积神经网络平台,专门针对医学图像处理分析,上一篇博客已经详细介绍了这个平台,接下来让我简单介绍一下目前我了解到的使用方法.更详细的使用方法.以及配置过程请查看NiftyN ...

  10. Java NIO- 最好文档

    http://www.cnblogs.com/puyangsky/p/5840873.html 1 背景介绍 在上一篇文章中我们介绍了Java基本IO,也就是阻塞式IO(BIO),在JDK1.4版本后 ...