AuthorizeAttribute 过滤器上的应用程序令牌错误
Wrong application token on AuthorizeAttribute Filter
在我的 AJAX POST 中,我发送 AntiForgeryToken 来验证我的控制器操作,为此我使用 AuthorizeAttribute 过滤器来验证它。
它在本地机器上工作得很好。但是,在服务器上,它会验证同一 server/domain.
上另一个站点在浏览器上注册的第一个 AntiForgeryToken
我的 AuthorizeAttribute 过滤器:
public void OnAuthorization(AuthorizationContext filterContext)
{
if(filterContext == null)
throw new HttpAntiForgeryException("filterContext is null");
if (filterContext.RequestContext == null)
throw new HttpAntiForgeryException("filterContext.RequestContext is null");
if (filterContext.RequestContext.HttpContext == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext is null");
if (filterContext.RequestContext.HttpContext.Request == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext.Request is null");
if (filterContext.RequestContext.HttpContext.Request.Headers == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext.Request.Headers is null");
if (filterContext.RequestContext.HttpContext.Request.Cookies == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext.Request.Cookies is null");
string KEY_NAME = "__RequestVerificationToken";
string clientToken = filterContext.RequestContext.HttpContext.Request.Headers.Get(KEY_NAME);
if (clientToken == null)
{
throw new HttpAntiForgeryException(string.Format("Header does not contain {0}", KEY_NAME));
}
var key = filterContext.HttpContext.Request.Cookies.AllKeys.FirstOrDefault(m => m == KEY_NAME);
if (string.IsNullOrEmpty(key))
key = filterContext.HttpContext.Request.Cookies.AllKeys.FirstOrDefault(m => m != null && m.Contains(KEY_NAME + "_"));
if (string.IsNullOrEmpty(key))
key = filterContext.HttpContext.Request.Cookies.AllKeys.FirstOrDefault(m => m != null && m.Contains(KEY_NAME));
var serverToken = filterContext.HttpContext.Request.Cookies.Get(key ?? KEY_NAME);
if (serverToken == null)
{
throw new HttpAntiForgeryException(string.Format("Cookies does not contain {0}", KEY_NAME));
}
System.Web.Helpers.AntiForgery.Validate(serverToken.Value, clientToken);
}
我的 AJAX 请求:
$.ajax({
type: !methodType ? "POST" : methodType,
url: methodName,
data: methodType == "get" || methodType == "GET" ? parameters : typeof parameters !== "undefined" && parameters != null ? JSON.stringify(parameters) : null,
contentType: "application/json; charset=utf-8",
dataType: "json",
headers: {
"__RequestVerificationToken": token
},
processdata: true,
success: succeededHandler,
async: isAsync,
crossDomain: false,
error: errorHandler
})
.done(function (data) {
if (doneHandler)
doneHandler();
})
.always(function (xhr) {
closeLoadingDialog();
try {
if (xhr.status == 401 || (xhr.getResponseHeader("X-Responded-JSON") != null
&& JSON.parse(xhr.getResponseHeader("X-Responded-JSON")).status == "401")) {
(location ? location : window.location).reload();
return;
}
}
catch (er) { }
});
我已经发现问题可能出在我用 FirstOrDefault()
搜索 serverToken
key
的方式上。这是在寻找在浏览器上注册的所有 cookie,而不仅仅是应用程序 cookie。
但是我如何才能只查找应用程序 cookie?如何在我的 AuthorizeAttribute 过滤器上解决这个问题?
经过多次搜索和头脑风暴后,我发现这个 link 显示了一种类似的验证 AntiForgeryToken 的方法。但是这里他们使用 AntiForgeryConfig.CookieName
属性 来获取应用程序 cookie。
他们说:
CookieName is a static property on the class
System.Web.Helpers.AntiForgeryConfig.
You can see more here:
http://msdn.microsoft.com/en-us/library/system.web.helpers.antiforgeryconfig.cookiename(v=vs.111).aspx
所以我将代码更改为:
public void OnAuthorization(AuthorizationContext filterContext)
{
string KEY_NAME = "__RequestVerificationToken";
if (filterContext == null)
throw new HttpAntiForgeryException("filterContext is null");
if (filterContext.RequestContext == null)
throw new HttpAntiForgeryException("filterContext.RequestContext is null");
if (filterContext.RequestContext.HttpContext == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext is null");
if (filterContext.RequestContext.HttpContext.Request == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext.Request is null");
if (filterContext.RequestContext.HttpContext.Request.Headers == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext.Request.Headers is null");
if (filterContext.RequestContext.HttpContext.Request.Cookies == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext.Request.Cookies is null");
var request = filterContext.HttpContext.Request;
// Ajax POSTs and normal form posts have to be treated differently when it comes to validating the AntiForgeryToken
if (request.IsAjaxRequest())
{
string clientToken = request.Headers.Get(KEY_NAME);
if (clientToken == null)
{
throw new HttpAntiForgeryException(string.Format("Header does not contain {0}", KEY_NAME));
}
var serverToken = request.Cookies[AntiForgeryConfig.CookieName];
var cookieValue = serverToken != null ? serverToken.Value : KEY_NAME;
AntiForgery.Validate(cookieValue, clientToken);
}
else
{
new ValidateAntiForgeryTokenAttribute().OnAuthorization(filterContext);
}
}
现在工作得很好!
在我的 AJAX POST 中,我发送 AntiForgeryToken 来验证我的控制器操作,为此我使用 AuthorizeAttribute 过滤器来验证它。
它在本地机器上工作得很好。但是,在服务器上,它会验证同一 server/domain.
上另一个站点在浏览器上注册的第一个 AntiForgeryToken我的 AuthorizeAttribute 过滤器:
public void OnAuthorization(AuthorizationContext filterContext)
{
if(filterContext == null)
throw new HttpAntiForgeryException("filterContext is null");
if (filterContext.RequestContext == null)
throw new HttpAntiForgeryException("filterContext.RequestContext is null");
if (filterContext.RequestContext.HttpContext == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext is null");
if (filterContext.RequestContext.HttpContext.Request == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext.Request is null");
if (filterContext.RequestContext.HttpContext.Request.Headers == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext.Request.Headers is null");
if (filterContext.RequestContext.HttpContext.Request.Cookies == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext.Request.Cookies is null");
string KEY_NAME = "__RequestVerificationToken";
string clientToken = filterContext.RequestContext.HttpContext.Request.Headers.Get(KEY_NAME);
if (clientToken == null)
{
throw new HttpAntiForgeryException(string.Format("Header does not contain {0}", KEY_NAME));
}
var key = filterContext.HttpContext.Request.Cookies.AllKeys.FirstOrDefault(m => m == KEY_NAME);
if (string.IsNullOrEmpty(key))
key = filterContext.HttpContext.Request.Cookies.AllKeys.FirstOrDefault(m => m != null && m.Contains(KEY_NAME + "_"));
if (string.IsNullOrEmpty(key))
key = filterContext.HttpContext.Request.Cookies.AllKeys.FirstOrDefault(m => m != null && m.Contains(KEY_NAME));
var serverToken = filterContext.HttpContext.Request.Cookies.Get(key ?? KEY_NAME);
if (serverToken == null)
{
throw new HttpAntiForgeryException(string.Format("Cookies does not contain {0}", KEY_NAME));
}
System.Web.Helpers.AntiForgery.Validate(serverToken.Value, clientToken);
}
我的 AJAX 请求:
$.ajax({
type: !methodType ? "POST" : methodType,
url: methodName,
data: methodType == "get" || methodType == "GET" ? parameters : typeof parameters !== "undefined" && parameters != null ? JSON.stringify(parameters) : null,
contentType: "application/json; charset=utf-8",
dataType: "json",
headers: {
"__RequestVerificationToken": token
},
processdata: true,
success: succeededHandler,
async: isAsync,
crossDomain: false,
error: errorHandler
})
.done(function (data) {
if (doneHandler)
doneHandler();
})
.always(function (xhr) {
closeLoadingDialog();
try {
if (xhr.status == 401 || (xhr.getResponseHeader("X-Responded-JSON") != null
&& JSON.parse(xhr.getResponseHeader("X-Responded-JSON")).status == "401")) {
(location ? location : window.location).reload();
return;
}
}
catch (er) { }
});
我已经发现问题可能出在我用 FirstOrDefault()
搜索 serverToken
key
的方式上。这是在寻找在浏览器上注册的所有 cookie,而不仅仅是应用程序 cookie。
但是我如何才能只查找应用程序 cookie?如何在我的 AuthorizeAttribute 过滤器上解决这个问题?
经过多次搜索和头脑风暴后,我发现这个 link 显示了一种类似的验证 AntiForgeryToken 的方法。但是这里他们使用 AntiForgeryConfig.CookieName
属性 来获取应用程序 cookie。
他们说:
CookieName is a static property on the class System.Web.Helpers.AntiForgeryConfig.
You can see more here: http://msdn.microsoft.com/en-us/library/system.web.helpers.antiforgeryconfig.cookiename(v=vs.111).aspx
所以我将代码更改为:
public void OnAuthorization(AuthorizationContext filterContext)
{
string KEY_NAME = "__RequestVerificationToken";
if (filterContext == null)
throw new HttpAntiForgeryException("filterContext is null");
if (filterContext.RequestContext == null)
throw new HttpAntiForgeryException("filterContext.RequestContext is null");
if (filterContext.RequestContext.HttpContext == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext is null");
if (filterContext.RequestContext.HttpContext.Request == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext.Request is null");
if (filterContext.RequestContext.HttpContext.Request.Headers == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext.Request.Headers is null");
if (filterContext.RequestContext.HttpContext.Request.Cookies == null)
throw new HttpAntiForgeryException("filterContext.RequestContext.HttpContext.Request.Cookies is null");
var request = filterContext.HttpContext.Request;
// Ajax POSTs and normal form posts have to be treated differently when it comes to validating the AntiForgeryToken
if (request.IsAjaxRequest())
{
string clientToken = request.Headers.Get(KEY_NAME);
if (clientToken == null)
{
throw new HttpAntiForgeryException(string.Format("Header does not contain {0}", KEY_NAME));
}
var serverToken = request.Cookies[AntiForgeryConfig.CookieName];
var cookieValue = serverToken != null ? serverToken.Value : KEY_NAME;
AntiForgery.Validate(cookieValue, clientToken);
}
else
{
new ValidateAntiForgeryTokenAttribute().OnAuthorization(filterContext);
}
}
现在工作得很好!