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有很多实现和思想值得我们思考.在深入源码之前,我们可以相关注下一些常见问题,这样带着问题去看实现,也能更加清晰的了解. 常见问题 大概看了下主 ...
随机推荐
- [POI2007]堆积木Klo
题解: dp定义方程的时候 好像也不能都用前一个来递推..这样就不能优化了 这题看了题解才想出来... 还是很简单的啊.... 我们定义f[i]表示前i个最大收益 那么j要能从i转移就得满足a[i]- ...
- 2018软工实践作业八之UML设计
1. 团队信息 队名:小白吃队 成员: 后敬甲 031602409 卢泽明 031602328 蔡文斌 031602301 葛亮 031602617 刘浩 031602423 黄泽 031602317 ...
- python全栈开发day60-django_app ORM 完整登录案例
day60 内容回顾: 1. HTTP协议: 1.请求(浏览器发给服务端的消息——request) 请求方法 URL 协议版本\r\n k1:v1\r\n k2:v2\r\n \r\n 请求体 —— ...
- Flink运行在yarn上
在一个企业中,为了最大化的利用集群资源,一般都会在一个集群中同时运行多种类型的 Workload.因此 Flink 也支持在 Yarn 上面运行: flink on yarn的前提是:hdfs.yar ...
- java读取配置文件方法以及工具类
第一种方式 : java工具类读取配置文件工具类 只是案例代码 抓取异常以后的代码自己处理 import java.io.FileNotFoundException; import java.io. ...
- spring ,springmvc,mybatis 最基本的整合,没有多余的jar包和依赖 2018.9.29日
最基本的ssm框架整合 本案例采用2018商业版intellij idea 编辑器 maven项目管理工具 tomcat8.5 接着上一篇使用springmvc最基本配置开始 https: ...
- docker删除名字为none的imgae
docker rmi $(docker images -f "dangling=true" -q)
- rest模式get,post,put,delete简单讲解
1.请求方法为get时,会向数据库请求数据,类似于select语言,只能达到查询的效果,不会添加,修改,不会影响资源的内容,无副作用 2.请求方法为post时,该方法,用于向服务器添加数据,可以改变数 ...
- ActiveMQ在windows下启动失败解决方案
activemq.xml文件中的 <transportConnectors> <!-- DOS protection, limit concurrent connections to ...
- Spark-Unit2-Spark交互式命令行与SparkWordCount
一.Spark交互式命令行 启动脚本:spark-shell 先启动spark:./start-all.sh 本地模式启动命令:/bin/spark-shell 集群模式启动命令:/bin/spark ...