zoukankan      html  css  js  c++  java
  • 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 * 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中获取)都是可以在负载中有效。

    所以放心在负载中使用,增加你的程序的安全性。

  • 相关阅读:
    常见Dos命令
    常用快捷键小技巧
    springboot集成JPA返回Json报错 com.fasterxml.jackson.data
    docker安装mysql 8.0.20 版本 超详细教程
    8.24 Java自学
    8.23 Java自学
    8.22 Java自学
    8.21 Java自学
    8.20 Java自学
    8.19 Java自学
  • 原文地址:https://www.cnblogs.com/ly7454/p/4246318.html
Copyright © 2011-2022 走看看