Skip to content

Commit

Permalink
Added support for including an antiforgery token if one is rendered o…
Browse files Browse the repository at this point in the history
…n the page
  • Loading branch information
haacked committed Oct 11, 2011
1 parent 25df45f commit bf4a15d
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 31 deletions.
@@ -1,4 +1,5 @@
using System.Collections;
using System.Web.Mvc;
using MvcHaack.Ajax.Sample.Models;

namespace MvcHaack.Ajax.Sample.Areas.AjaxDemo.Controllers {
Expand All @@ -15,5 +16,9 @@ public class ComicsDemoController : JsonController {
return new { message = "Saved!", comic = book };
}

[ValidateJsonAntiForgeryToken]
public object SaveSecure(ComicBook book) {
return new { message = "Saved!", comic = book };
}
}
}
Expand Up @@ -14,6 +14,10 @@ public class HomeController : Controller {
return View();
}

public ActionResult SecureComicsPostDemo() {
return View();
}

public ActionResult KnockoutDemo() {
return View();
}
Expand Down
Expand Up @@ -7,5 +7,6 @@
<ul>
<li><a href="@Url.Action("ComicsDemo")">Simple MVC JSON Proxy Demo - Retrieving Data</a></li>
<li><a href="@Url.Action("ComicsPostDemo")">Simple MVC JSON Proxy Demo - Posting Data</a></li>
<li><a href="@Url.Action("SecureComicsPostDemo")">MVC JSON Proxy Demo - Posting Data With AntiForgery Token</a></li>
<li><a href="@Url.Action("KnockoutDemo")">Using Knockout with the MVC JSON Proxy</a></li>
</ul>
@@ -0,0 +1,53 @@
@{
ViewBag.Title = "ComicsDemoPostback";
}

@section Scripts {
<script src="@Url.Content("~/Scripts/jquery-1.6.2.js")" type="text/javascript"></script>
<script src="@Url.Content("~/json/comicsdemo?json")"></script>
<script src="@Url.Content("~/Scripts/comicsdemo.js")"></script>
}

<h2>Simple Comics Post Demo With AntiForgery Protection</h2>

<button id="post">Click to post a comic to the server</button>

<script>
$(function () {
$('#post').click(function () {
$mvc.ComicsDemo.SaveSecure({ Title: 'Adventurers', Issue: 123 }, true /*includeAntiForgeryToken*/).done(function (data) {
alert(data.message + ' Comic: ' + data.comic.Title);
}).fail(function () { alert('Failed!'); });
});
});
</script>

<hr />

@Html.AntiForgeryToken()

<fieldset class="code">
<legend>comicspostdemo.html</legend>

<pre class="csharpcode">
<span class="kwrd">&lt;</span><span class="html">script</span> <span class="attr">src</span><span class="kwrd">="../../Scripts/jquery-1.6.2.js"</span> <span class="attr">type</span><span class="kwrd">="text/javascript"</span><span class="kwrd">&gt;&lt;/</span><span class="html">script</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">script</span> <span class="attr">src</span><span class="kwrd">="/json/comicsdemo?json"</span><span class="kwrd">&gt;&lt;/</span><span class="html">script</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">script</span> <span class="attr">src</span><span class="kwrd">="/Scripts/comicsdemo.js"</span><span class="kwrd">&gt;&lt;/</span><span class="html">script</span><span class="kwrd">&gt;</span>

<span class="kwrd">&lt;</span><span class="html">h2</span><span class="kwrd">&gt;</span>Simple Comics Post Demo<span class="kwrd">&lt;/</span><span class="html">h2</span><span class="kwrd">&gt;</span>

<span class="kwrd">&lt;</span><span class="html">button</span> <span class="attr">id</span><span class="kwrd">="post"</span><span class="kwrd">&gt;</span>Click to post a comic to the server<span class="kwrd">&lt;/</span><span class="html">button</span><span class="kwrd">&gt;</span>

<span class="asp">@@</span>Html.AntiForgeryToken()

<span class="kwrd">&lt;</span><span class="html">script</span><span class="kwrd">&gt;</span>
$(<span class="kwrd">function</span>() {
$(<span class="str">'#post'</span>).click(<span class="kwrd">function</span>() {
$mvc.ComicsDemo.SaveSecure({ Title: <span class="str">'Adventurers'</span>, Issue: 123 }).done(<span class="kwrd">function</span>(data) {
alert(data.message + <span class="str">' Comic: '</span> + data.comic.Title);
});
});
});
<span class="kwrd">&lt;/</span><span class="html">script</span><span class="kwrd">&gt;</span></pre>

</fieldset>
1 change: 1 addition & 0 deletions src/MvcHaack.Ajax.DemoWeb/MvcHaack.Ajax.DemoWeb.csproj
Expand Up @@ -116,6 +116,7 @@
<Content Include="Web.Release.config">
<DependentUpon>Web.config</DependentUpon>
</Content>
<Content Include="Areas\MvcAjaxDemo\Views\Home\SecureComicsPostDemo.cshtml" />
<None Include="_bin_deployableAssemblies\Microsoft.Web.Infrastructure.dll" />
<None Include="_bin_deployableAssemblies\System.Web.WebPages.Razor.dll" />
<None Include="_bin_deployableAssemblies\System.Web.WebPages.dll" />
Expand Down
54 changes: 27 additions & 27 deletions src/MvcHaack.Ajax.DemoWeb/Web.config
@@ -1,60 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=152368
-->
<configuration>
<appSettings>
<add key="webpages:Version" value="1.0.0.0" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="webpages:Version" value="1.0.0.0"/>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
<system.web>
<compilation debug="false" targetFramework="4.0">
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</assemblies>
</compilation>
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880" />
<forms loginUrl="~/Account/LogOn" timeout="2880"/>
</authentication>
<pages>
<namespaces>
<add namespace="System.Web.Helpers" />
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="System.Web.WebPages" />
<add namespace="System.Web.Helpers"/>
<add namespace="System.Web.Mvc"/>
<add namespace="System.Web.Mvc.Ajax"/>
<add namespace="System.Web.Mvc.Html"/>
<add namespace="System.Web.Routing"/>
<add namespace="System.Web.WebPages"/>
</namespaces>
</pages>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true" />
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true"/>
<defaultDocument>
<files>
<clear />
<add value="default.html" />
</files>
<files>
<clear/>
<add value="default.html"/>
</files>
</defaultDocument>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0.0.0" />
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.data>
<DbProviderFactories>
<remove invariant="System.Data.SqlServerCe.4.0" />
<add name="Microsoft SQL Server Compact Data Provider 4.0" invariant="System.Data.SqlServerCe.4.0" description=".NET Framework Data Provider for Microsoft SQL Server Compact" type="System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" />
<remove invariant="System.Data.SqlServerCe.4.0"/>
<add name="Microsoft SQL Server Compact Data Provider 4.0" invariant="System.Data.SqlServerCe.4.0" description=".NET Framework Data Provider for Microsoft SQL Server Compact" type="System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"/>
</DbProviderFactories>
</system.data>
</configuration>
10 changes: 8 additions & 2 deletions src/MvcHaack.Ajax/JsonActionInvoker.cs
Expand Up @@ -37,12 +37,18 @@ public class JsonActionInvoker : ControllerActionInvoker {
$.each({1}, function(action) {{
var action = this;
$mvc.{0}[this] = function(obj) {{
$mvc.{0}[this] = function(obj, includeAntiForgeryToken) {{
var headers = {{'x-mvc-action': action}};
if (includeAntiForgeryToken) {{
headers['__RequestVerificationToken'] = $('input[name=""__RequestVerificationToken""]').val();
}}
return $.ajax({{
cache: false,
dataType: 'json',
type: 'POST',
headers: {{'x-mvc-action': action}},
headers: headers,
data: window.JSON.stringify(obj),
contentType: 'application/json; charset=utf-8',
url: '{2}&action=' + action
Expand Down
2 changes: 2 additions & 0 deletions src/MvcHaack.Ajax/MvcHaack.Ajax.csproj
Expand Up @@ -38,6 +38,7 @@
<Reference Include="System.Web" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
Expand All @@ -49,6 +50,7 @@
<Compile Include="JsonController.cs" />
<Compile Include="JsonRoute.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ValidateJsonAntiForgeryTokenAttribute.cs" />
</ItemGroup>
<ItemGroup>
<None Include="MvcHaack.Ajax.nuspec" />
Expand Down
4 changes: 2 additions & 2 deletions src/MvcHaack.Ajax/Properties/AssemblyInfo.cs
Expand Up @@ -31,5 +31,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.1.0")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyFileVersion("1.1.0.0")]
57 changes: 57 additions & 0 deletions src/MvcHaack.Ajax/ValidateJsonAntiForgeryTokenAttribute.cs
@@ -0,0 +1,57 @@
using System;
using System.Collections.Specialized;
using System.Web;
using System.Web.Helpers;
using System.Web.Mvc;

namespace MvcHaack.Ajax {
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class ValidateJsonAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter {

public void OnAuthorization(AuthorizationContext filterContext) {
if (filterContext == null) {
throw new ArgumentNullException("filterContext");
}

var httpContext = new JsonAntiForgeryHttpContextWrapper(HttpContext.Current);
AntiForgery.Validate(httpContext, Salt ?? string.Empty);
}

public string Salt {
get;
set;
}

private class JsonAntiForgeryHttpContextWrapper : HttpContextWrapper {
HttpRequestBase _request;
public JsonAntiForgeryHttpContextWrapper(HttpContext httpContext)
: base(httpContext) {
_request = new JsonAntiForgeryHttpRequestWrapper(httpContext.Request);
}

public override HttpRequestBase Request {
get {
return _request;
}
}
}

private class JsonAntiForgeryHttpRequestWrapper : HttpRequestWrapper {
NameValueCollection _form;

public JsonAntiForgeryHttpRequestWrapper(HttpRequest request)
: base(request) {
_form = new NameValueCollection(request.Form);
if (request.Headers["__RequestVerificationToken"] != null) {
_form["__RequestVerificationToken"] = request.Headers["__RequestVerificationToken"];
}
}

public override NameValueCollection Form {
get {
return _form;
}
}
}
}
}

1 comment on commit bf4a15d

@yuanrui
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the ValidateJsonAntiForgeryTokenAttribute rename ValidateAjaxAntiForgeryTokenAttribute will be better.

Please sign in to comment.