首页 > 代码库 > mvc中AntiForgeryToken的实现方式--看Mvc源码

mvc中AntiForgeryToken的实现方式--看Mvc源码

通过 AntiForgeryWorker的GetHtml()方法生成html --input hide元素--value=http://www.mamicode.com/要验证的值,并生成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 =http://www.mamicode.com/ 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 =http://www.mamicode.com/ 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 =http://www.mamicode.com/ 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 * 4); foreach (byte b in Convert.FromBase64String(base64)) { builder.Append(HexDigit(b >> 4)); builder.Append(HexDigit(b & 0x0F)); } string result = builder.ToString(); return result; } private static char HexDigit(int value) { return (char)(value > 9 ? value + 7 : value + 0); } private static int HexValue(char digit) { return digit > 9 ? digit - 7 : digit - 0; } private static string HexToBase64(string hex) { int size = hex.Length / 2; byte[] bytes = new byte[size]; for (int idx = 0; idx < size; idx++) { bytes[idx] = (byte)((HexValue(hex[idx * 2]) << 4) + HexValue(hex[idx * 2 + 1])); } string result = Convert.ToBase64String(bytes); return result; } }

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

internal sealed class AntiForgeryData {        private const string AntiForgeryTokenFieldName = "__RequestVerificationToken";        private const int TokenLength = 128 / 8;        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源码