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有很多实现和思想值得我们思考.在深入源码之前,我们可以相关注下一些常见问题,这样带着问题去看实现,也能更加清晰的了解. 常见问题 大概看了下主 ...
随机推荐
- Pytorch LSTM 词性判断
首先,我们定义好一个LSTM网络,然后给出一个句子,每个句子都有很多个词构成,每个词可以用一个词向量表示,这样一句话就可以形成一个序列,我们将这个序列依次传入LSTM,然后就可以得到与序列等长的输出, ...
- Bootstraptable源码
// @author 文志新 http://jsfiddle.net/wenyi/47nz7ez9/3/ /**关于插件的通用构造 * * 构造函数PluginName($trigger,option ...
- 公共语言运行时支持(/clr)
项目属性 -> 配置属性 -> “常规”里开启“公共语言运行时支持(/clr)
- asp.net mvc自动压缩文件,并生成CDN引用
很多站点都是用了静态文件分离.我推荐一种处理静态文件分离的方式. BundleExtensions.cs public static class BundleExtensions { public s ...
- python全栈开发day52-bootstrap的运用
1. css样式 2. 插件 3. 创建一个项目的步骤 1) npm init --yes 或 npm init -y npm init npm init:这个命令用于创建一个package.js ...
- kafka其中一台节点坏掉的迁移或者数据迁移
kafka版本:适用于目前2.0以下 第一步: 假如有一个topic叫做test,当前topic的详情是这样的: [cdh@cdh1 kafka_2.11-1.0.1]$ bin/kafka-topi ...
- jav实验二
实验内容 1.初步掌握单元测试和TDD 2.理解并掌握面向对象三要素:封装.继承.多态 3.初步掌握UML建模 4.熟悉S.O.L.I.D原则 5.了解设计模式 实验内容 1.参考Intellj ID ...
- Codeforces Round #517 体验记
原文链接 https://www.cnblogs.com/zhouzhendong/p/CF1071.html 赛前: 呀,这个 Round # 必须打啊. 于是临时改变注意决定打这一场.用小号打. ...
- POJ 2488 A Knight's Journey-dfs
题目链接:http://poj.org/problem?id=2488 题目解读:首先得弄清楚国际象棋中关于“马走日”的规则,如上图中的马,它的下一步的走法有8中,所以对每一个位置的马,它所能走的8个 ...
- webpack的总结
1,首先 项目的入口----package的入口 "scripts": { "dev": "node build/dev-server.js" ...