mvc中AntiForgeryToken的实现方式--看Mvc源码
通过 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源码的更多相关文章
- asp.net开发中常见公共捕获异常方式总结(附源码)
本文实例总结了asp.net开发中常见公共捕获异常方式.分享给大家供大家参考,具体如下: 前言:在实际开发过程中,对于一个应用系统来说,应该有自己的一套成熟的异常处理框架,这样当异常发生时,也能得到统 ...
- 一点一点看JDK源码(〇)
一点一点看JDK源码(〇) liuyuhang原创,未经允许进制转载 写在前面: 几乎所有的大神都会强调看源码,也强调源码的重要性: 但是如何看源码,源码看什么?看了什么用?看了怎么用? 困扰很多人, ...
- 一点一点看JDK源码(一)Collection体系概览
一点一点看JDK源码(一)Collection体系概览 liuyuhang原创,未经允许进制转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.综述 Collection为集 ...
- 一点一点看JDK源码(二)java.util.List
一点一点看JDK源码(二)java.util.List liuyuhang原创,未经允许进制转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.综述 List译为表,一览表, ...
- 一点一点看JDK源码(四)java.util.ArrayList 中篇
一点一点看JDK源码(四)java.util.ArrayList 中篇 liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.综述 在前篇中 ...
- 一点一点看JDK源码(五)java.util.ArrayList 后篇之sort与Comparator
一点一点看JDK源码(五)java.util.ArrayList 后篇之sort与Comparator liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JD ...
- 一点一点看JDK源码(六)java.util.LinkedList前篇之链表概要
一点一点看JDK源码(六)java.util.LinkedList前篇之链表概要 liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.什么 ...
- 零基础带你看Spring源码——IOC控制反转
本章开始来学习下Spring的源码,看看Spring框架最核心.最常用的功能是怎么实现的. 网上介绍Spring,说源码的文章,大多数都是生搬硬推,都是直接看来的观点换个描述就放出来.这并不能说有问题 ...
- 带着问题看redux源码
前言 作为前端状态管理器,这个比较跨时代的工具库redux有很多实现和思想值得我们思考.在深入源码之前,我们可以相关注下一些常见问题,这样带着问题去看实现,也能更加清晰的了解. 常见问题 大概看了下主 ...
随机推荐
- JS高级程序设计3
PS:有一小部分写在了 JS 2017了 JSON <!DOCTYPE html> <html lang="en"> <head> <me ...
- 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 ...
- linux重启服务的脚本命令
最近做网站测试,每次测试完成都要重启服务,为此写了一个简单的shell脚本 linux服务重启shell脚本示例 2014年12月18日 linux服务重启脚本,如何实现linux服务的定时重启,可以 ...
- windows server远程连接提示“终端服务器超出了最大允许连接”
- linux中通过lsof恢复删除的文件,前题是fd被占用。
http://www.serverwatch.com/tutorials/article.php/3822816/Recovering-Deleted-Files-With-lsof.htm One ...
- Cloudera Manager的安装
1. cloudera manager的概念 简单来说,Cloudera Manager是一个拥有集群自动化安装.中心化管理.集群监控.报警功能的一个工具(软件),使得安装集群从几天的时间缩短在几个 ...
- union表关联模糊查询servlet,action方法
2018-11-14 servletxml层 public String getSql(String keyword) { StringBuffer sqlSb = new StringBuffer( ...
- HDU 5178 pairs【二分】||【尺取】
<题目链接> 题目大意: 给定一个整数序列,求出绝对值小于等于k的有序对个数. 解题分析: $O(nlong(n))$的二分很好写,这里就不解释了.本题尺取$O(n)$也能做,并且效率很不 ...
- NiftyNet开源平台使用
NiftyNet是一款开源的卷积神经网络平台,专门针对医学图像处理分析,上一篇博客已经详细介绍了这个平台,接下来让我简单介绍一下目前我了解到的使用方法.更详细的使用方法.以及配置过程请查看NiftyN ...
- Java NIO- 最好文档
http://www.cnblogs.com/puyangsky/p/5840873.html 1 背景介绍 在上一篇文章中我们介绍了Java基本IO,也就是阻塞式IO(BIO),在JDK1.4版本后 ...