如何在不同域上的MVC前端和Web Api之间实现Anti-Forgery Token?

时间:2015-10-29 09:32:22

标签: asp.net-mvc asp.net-web-api antiforgerytoken

  1. 我希望在www.example1.com
  2. 上安装MVC项目
  3. api.example2.com
  4. 上的WebApi项目

    我想限制对WebApi的访问。 我试图实施反伪造令牌:

    当我使用Anti-forgery令牌向WebApi创建GET请求时,我得到一个例外,因为请求不包含此令牌。

    名为ValidateRequestHeader的方法是变量cookie = null

    如何修复以下代码?这是正确的解决方案吗?

    MVC项目(前端) - 用于开发是localhost:33635

    Index.cshtml

    <div class="container">
    
    
        <div class="row">
    
            <div class="col-md-12">
    
    
                <input id="get-request-button" type="button" class="btn btn-info" value="Create request to API Server" />
    
                <br />
    
                <div id="result"></div>
    
            </div>
    
    
        </div>
    
    
    </div>
    
    
    @section scripts
    {
    
        <script type="text/javascript">
    
            @functions{
                public string TokenHeaderValue()
                {
                    string cookieToken, formToken;
                    AntiForgery.GetTokens(null, out cookieToken, out formToken);
                    return cookieToken + ":" + formToken;
                }
            }
    
            $(function () {
    
                $("#get-request-button").click(function () {
    
                    $.ajax("http://localhost:33887/api/values", {
                        type: "GET",
                        contentType: "application/json",
                        data: {},
                        dataType: "json",
                        headers: {
                            'RequestVerificationToken': '@TokenHeaderValue()'
                        }
                    }).done(function (data) {
                        $("#result").html(data);
                    });
    
                    return false;
                });
    
            });
    
    
        </script>
    
    }
    

    WebApi项目 - 用于开发是localhost:33887

    WebApiConfig.cs

    public static void Register(HttpConfiguration config)
            {
                // Web API configuration and services
    
                config.EnableCors(new EnableCorsAttribute("http://localhost:33635", "*", "*"));
    
                // Web API routes
                config.MapHttpAttributeRoutes();
    
                config.Routes.MapHttpRoute(
                        name: "DefaultApi",
                        routeTemplate: "api/{controller}/{id}",
                        defaults: new { id = RouteParameter.Optional }
                );
            }
    

    ValidateHttpAntiForgeryTokenAttribute.cs:

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
        public sealed class ValidateHttpAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
        {
            public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
            {
                var request = actionContext.Request;
    
                try
                {
                    if (IsAjaxRequest(request))
                    {
                        ValidateRequestHeader(request);
                    }
                    else
                    {
                        AntiForgery.Validate();
                    }
                }
                catch (Exception)
                {
                    actionContext.Response = new HttpResponseMessage
                    {
                        StatusCode = HttpStatusCode.Forbidden,
                        RequestMessage = actionContext.ControllerContext.Request
                    };
                    return FromResult(actionContext.Response);
                }
                return continuation();
            }
    
            private Task<HttpResponseMessage> FromResult(HttpResponseMessage result)
            {
                var source = new TaskCompletionSource<HttpResponseMessage>();
                source.SetResult(result);
                return source.Task;
            }
    
            private bool IsAjaxRequest(HttpRequestMessage request)
            {
                IEnumerable<string> xRequestedWithHeaders;
                if (!request.Headers.TryGetValues("X-Requested-With", out xRequestedWithHeaders)) return false;
    
                var headerValue = xRequestedWithHeaders.FirstOrDefault();
    
                return !String.IsNullOrEmpty(headerValue) && String.Equals(headerValue, "XMLHttpRequest", StringComparison.OrdinalIgnoreCase);
            }
    
            private void ValidateRequestHeader(HttpRequestMessage request)
            {
                var headers = request.Headers;
                var cookie = headers
                        .GetCookies()
                        .Select(c => c[AntiForgeryConfig.CookieName])
                        .FirstOrDefault();
    
                IEnumerable<string> xXsrfHeaders;
    
                if (headers.TryGetValues("RequestVerificationToken", out xXsrfHeaders))
                {
                    var rvt = xXsrfHeaders.FirstOrDefault();
    
                    if (cookie == null)
                    {
                        throw new InvalidOperationException($"Missing {AntiForgeryConfig.CookieName} cookie");
                    }
    
                    AntiForgery.Validate(cookie.Value, rvt);
                }
                else
                {
                    var headerBuilder = new StringBuilder();
    
                    headerBuilder.AppendLine("Missing X-XSRF-Token HTTP header:");
    
                    foreach (var header in headers)
                    {
                        headerBuilder.AppendFormat("- [{0}] = {1}", header.Key, header.Value);
                        headerBuilder.AppendLine();
                    }
    
                    throw new InvalidOperationException(headerBuilder.ToString());
                }
            }
        }
    

    ValuesController:

    public class ValuesController : ApiController
        {
            // GET: api/Values
            [ValidateHttpAntiForgeryToken]
            public IEnumerable<string> Get()
            {
                return new string[] { "value1", "value2" };
            }
    
            // GET: api/Values/5
            public string Get(int id)
            {
                return "value";
            }
    
            // POST: api/Values
            public void Post([FromBody]string value)
            {
            }
    
            // PUT: api/Values/5
            public void Put(int id, [FromBody]string value)
            {
            }
    
            // DELETE: api/Values/5
            public void Delete(int id)
            {
            }
        }
    

1 个答案:

答案 0 :(得分:6)

这不是限制其他服务访问的方式 ForgeryToken 有助于防止CSRF攻击,ASP.NET MVC使用反伪造令牌,也称为请求验证令牌。客户端请求包含表单的HTML页面。服务器在响应中包含两个令牌。一个令牌作为cookie发送。提交表单时,这些表格将在同一台服务器上匹配。

我相信,您需要的是example1.comapi.example2.com的信任。 An example将是stackauth,它是整个Stack Exchange network中集中服务的域。然后,您可以根据需要在项目中实施授权。