Ajax 授权失败
Ajax Fails On Authorization
我正在构建至少部分数据传输依赖于 Ajax 的 MVC Web 应用程序。
控制器动作是
[RBAC]
[Authorize]
public string GetData(string inputdata)
{
some code ...
return jsondata;
}
ajax 调用是
$.ajax({
dataType: "json",
url: Url,
data: { '_inputdata': selectedText },
success: function (data)
{
response($.map(data,
function(item, index) {
return {
label: item.label,
value: item.value
}
}));
},
error: (function (jqXHR, textStatus, errorThrown, data) {
ProcessFail(jqXHR, textStatus, errorThrown, data);
});
})
});
[RBAC] 导致进行授权检查,这正是我想要的。
public override void OnAuthorization(AuthorizationContext filterContext)
{
......
filterContext.Result = new RedirectToRouteResult
(new RouteValueDictionary { { "action", "Index" },
{ "controller", "Unauthorised" } ,
{ "Area", String.Empty }});
.....
}
问题是我在 ajax 没有得到任何回报,除了失败。没有任何信息告诉我存在授权错误。
问题:
- 是否可以将授权失败的信息返回到 ajax 响应中。如果是这样怎么办?
- 如果 1. 的答案是否定的,我是否应该在拨打此电话之前检查此授权?
一如既往,感谢您的帮助。
看起来您使用的是 MVC 而不是 Web API,Web API 应该默认给您一个不错的 JSON 消息。
一个选项是检查响应的状态代码,如果身份验证失败,这应该会给你一个 401。
另一种方法是删除 [Authorize] 并在方法本身内部进行检查
public string GetData(string inputdata)
{
if (User.Identity.IsAuthenticated) {
return jsonData;
}
return failureJson;
}
注意:我确信有一种更好的方法可以做到这一点,但这应该可行
使用另一个参数 complete:
就像 success:
和 error:
一样在您的 $.ajax()
调用中检查授权失败。 success
后执行这段代码。这里 401 代码显示授权错误。检查 if 条件。
success: function (data)
{
response($.map(data,
function(item, index) {
return {
label: item.label,
value: item.value
}
}));
},
complete: function(jqXHR){
if (jqXHR.status== '401'){
**//Your code here whatever you want to do**
}
}
complete:
会在您的 ajax 调用完成时返回。
这是一个完整的解决方案,它允许您在操作中通过一次调用从根本上装饰您的操作,其工作方式与 ASP.net 中基于标准表单的身份验证的工作方式相同。
只需将电脑复制到此处即可。
该问题是通过装饰操作实现的授权代码不会将授权错误发送回 Ajax。
所以
[Authorize] or in my case [RBAC]
public string SomeActionCalledByAjax( some args)
{
some stuf
}
失败,没有向用户显示错误消息。
这是我实施的解决方案。它实际上使用了 OnAuthorization。
我的目标是获得一个简单的解决方案,使我能够像工厂授权代码一样装饰操作。我已经成功了。
归功于
How do I get the MethodInfo of an action, given action, controller and area names?
感谢 Miguel Angelo。
和
jQuery Ajax error handling, show custom exception messages
信用 AlexMAS
如果不是这些人,永远不会想出这个。
为了安全起见,我正在使用 RBAC。在这里找到它。
https://www.codeproject.com/articles/1079552/custom-roles-based-access-control-rbac-in-asp-ne
出色的基于角色的安全性。好的系统。它通过 ASP.NET 身份框架扩展了基于表单的身份验证。
因此,如果您可以在控制器外部看到 IPrincipal.User,这会很简单,但我发现我无法将其传递给控制器中的方法,但仍然看到用于 RBAC 的扩展该方法中的权限。
但是你可以在这里看到它。
public class RBACAttribute:AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
do stuff.
}
}
所以技巧变成了如何正确填充 AuthorizationContext filterContext 然后我可以调用 OnAuthorize。
这是 Miguel 代码的用武之地。它是对控制器的扩展。我稍微改变了它,因为它实际上会从传入的控制器引用中获取所有信息。我只想要 ActionDescriptor
所以我可以填充 AuthorizationContext 对象
public static class GetControllerAttr
{
public static ActionDescriptor GetActionAttributes(this Controller @this,string action,string controller,string area,string method)
{
var actionName = action ?? @this.RouteData.GetRequiredString("action");
var controllerName = controller ?? @this.RouteData.GetRequiredString("controller");
var areaName = area ?? @this.RouteData.Values [ "area" ] ;
var methodName = method ?? @this.RouteData.GetRequiredString("action");
var controllerFactory = ControllerBuilder.Current.GetControllerFactory();
var controllerContext = @this.ControllerContext;
var otherController = (ControllerBase)controllerFactory
.CreateController(
new RequestContext(controllerContext.HttpContext,new RouteData()),
controllerName);
var controllerDescriptor = new ReflectedControllerDescriptor(
otherController.GetType());
var controllerContext2 = new ControllerContext(
new MockHttpContextWrapper(
controllerContext.HttpContext.ApplicationInstance.Context,
methodName),
new RouteData(),
otherController);
var actionDescriptor = controllerDescriptor
.FindAction(controllerContext2,actionName);
return actionDescriptor ;
//var attributes = actionDescriptor.GetCustomAttributes(true)
// .Cast<Attribute>()
// .ToArray();
//return attributes;
}
}
class MockHttpContextWrapper:HttpContextWrapper
{
public MockHttpContextWrapper(HttpContext httpContext,string method)
: base(httpContext)
{
this.request = new MockHttpRequestWrapper(httpContext.Request,method);
}
private readonly HttpRequestBase request;
public override HttpRequestBase Request
{
get { return request; }
}
class MockHttpRequestWrapper:HttpRequestWrapper
{
public MockHttpRequestWrapper(HttpRequest httpRequest,string httpMethod)
: base(httpRequest)
{
this.httpMethod = httpMethod;
}
private readonly string httpMethod;
public override string HttpMethod
{
get { return httpMethod; }
}
}
}
我把Alex的代码稍微修改了一下,得到了我想发回给JQuery
的信息
public class ClientErrorHandler:FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
var response = filterContext.RequestContext.HttpContext.Response;
clsAuthorizationError _clsAuthorization = new clsAuthorizationError();
if(filterContext.Exception.Data.Contains("ErrorCode"))
{
_clsAuthorization.ErrorCode = (int)filterContext.Exception.Data["ErrorCode"];
_clsAuthorization.ReDirect = filterContext.Exception.Message;
string _results = JsonConvert.SerializeObject(_clsAuthorization);
response.Write(_results);
}
else
{
response.Write(filterContext.Exception.Message);
}
response.ContentType = MediaTypeNames.Text.Plain;
filterContext.ExceptionHandled = true;
}
}
public class clsAuthorizationError
{
public int ErrorCode { set; get; }
public string ReDirect { set; get; }
}
在覆盖的 OnAuthorization 方法中,我添加了 Url 字符串和错误代码。
public class RBACAttribute:AuthorizeAttribute
{
public string Url { set; get; }
public int ErrorCode { set; get; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
Url = null;
string _action = null;
string _controller = null;
try
{
if(!filterContext.HttpContext.Request.IsAuthenticated)
{
//Redirect user to login page if not yet authenticated. This is a protected resource!
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary { { "action",_action },
{ "controller",_controller },
{ "Area",String.Empty } });
Url = "/__controller__/__action__/";
Url = Url.Replace("__controller__",_controller);
Url = Url.Replace("__action__",_action);
ErrorCode = 401;
}
else
{
//Create permission string based on the requested controller name and action name in the format 'controllername-action'
string requiredPermission = String.Format("0}-{1}",
filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
filterContext.ActionDescriptor.ActionName);
if(!filterContext.HttpContext.User.HasPermission(requiredPermission) & !filterContext.HttpContext.User.IsSysAdmin())
{
_action = "Index";
_controller = "Unauthorised";
//User doesn't have the required permission and is not a SysAdmin, return our custom “401 Unauthorized” access error
//Since we are setting filterContext.Result to contain an ActionResult page, the controller's action will not be run.
//The custom “401 Unauthorized” access error will be returned to the browser in response to the initial request.
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary { { "action",_action },
{ "controller",_controller },
{ "Area",String.Empty } });
Url = "/__controller__/__action__/";
Url = Url.Replace("__controller__",_controller);
Url = Url.Replace("__action__",_action);
ErrorCode = 401;
}
//If the user has the permission to run the controller's action, the filterContext.Result will be uninitialized and
//executing the controller's action is dependant on whether filterContext.Result is uninitialized.
}
}
catch(Exception ex)
{
_action ="Error";
_controller = "Unauthorised";
Url = "/__controller__/__action__/";
Url = Url.Replace("__controller__",_controller);
Url = Url.Replace("__action__",_action);
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new { controller = _controller,action = _action,
_errorMsg = ex.Message })
ErrorCode = 500;
}
}
}
在 Ajax 调用中添加以下内容。
complete: function (jqXHR, textStatus, errorThrown)
{
var jparse = JSON.parse(jqXHR.responseText);
if (jparse.hasOwnProperty('ErrorCode'))
{
var code = jparse.ErrorCode;
var href = jparse.ReDirect;
window.location.href = href;
}
}
然后我创建了一个前端来组合 pc 的
public class clsOnAuthorization
{
//private string Redirect { set; get; }
//string _Action { set; get; }
//string _Controller { set; get; }
//string _url { set; get; }
AuthorizationContext _filterContext;
public clsOnAuthorization(Controller @this)
{
_filterContext = new AuthorizationContext(@this.ControllerContext,GetControllerAttr.GetActionAttributes(@this,null,null,null,null));
Verify ( ) ;
}
public void Verify()
{
RBACAttribute _rbacAttribute = new RBACAttribute();
_rbacAttribute.OnAuthorization(_filterContext);
if(_rbacAttribute.Url != null)
{
Exception myEx = new Exception(_rbacAttribute.Url);
myEx.Data.Add("ErrorCode", _rbacAttribute.ErrorCode);
throw myEx;
}
}
}
最后我装饰了动作,在动作中打了一个电话
[ClientErrorHandler]
public string JobGuid()
{
// send controller in with constructor.
clsOnAuthorization _clsOnAuthorization = new clsOnAuthorization(this);
// if authorization fails it raises and exception and never comes back here.
some stuff if authorization good.
}
通过一个修饰和一个 class 实例化,所有授权问题都消失了,我的 ajax 调用现在知道出了什么问题,可以适当地重定向。
我正在构建至少部分数据传输依赖于 Ajax 的 MVC Web 应用程序。
控制器动作是
[RBAC]
[Authorize]
public string GetData(string inputdata)
{
some code ...
return jsondata;
}
ajax 调用是
$.ajax({
dataType: "json",
url: Url,
data: { '_inputdata': selectedText },
success: function (data)
{
response($.map(data,
function(item, index) {
return {
label: item.label,
value: item.value
}
}));
},
error: (function (jqXHR, textStatus, errorThrown, data) {
ProcessFail(jqXHR, textStatus, errorThrown, data);
});
})
});
[RBAC] 导致进行授权检查,这正是我想要的。
public override void OnAuthorization(AuthorizationContext filterContext)
{
......
filterContext.Result = new RedirectToRouteResult
(new RouteValueDictionary { { "action", "Index" },
{ "controller", "Unauthorised" } ,
{ "Area", String.Empty }});
.....
}
问题是我在 ajax 没有得到任何回报,除了失败。没有任何信息告诉我存在授权错误。
问题:
- 是否可以将授权失败的信息返回到 ajax 响应中。如果是这样怎么办?
- 如果 1. 的答案是否定的,我是否应该在拨打此电话之前检查此授权?
一如既往,感谢您的帮助。
看起来您使用的是 MVC 而不是 Web API,Web API 应该默认给您一个不错的 JSON 消息。
一个选项是检查响应的状态代码,如果身份验证失败,这应该会给你一个 401。
另一种方法是删除 [Authorize] 并在方法本身内部进行检查
public string GetData(string inputdata)
{
if (User.Identity.IsAuthenticated) {
return jsonData;
}
return failureJson;
}
注意:我确信有一种更好的方法可以做到这一点,但这应该可行
使用另一个参数 complete:
就像 success:
和 error:
一样在您的 $.ajax()
调用中检查授权失败。 success
后执行这段代码。这里 401 代码显示授权错误。检查 if 条件。
success: function (data)
{
response($.map(data,
function(item, index) {
return {
label: item.label,
value: item.value
}
}));
},
complete: function(jqXHR){
if (jqXHR.status== '401'){
**//Your code here whatever you want to do**
}
}
complete:
会在您的 ajax 调用完成时返回。
这是一个完整的解决方案,它允许您在操作中通过一次调用从根本上装饰您的操作,其工作方式与 ASP.net 中基于标准表单的身份验证的工作方式相同。
只需将电脑复制到此处即可。
该问题是通过装饰操作实现的授权代码不会将授权错误发送回 Ajax。
所以
[Authorize] or in my case [RBAC]
public string SomeActionCalledByAjax( some args)
{
some stuf
}
失败,没有向用户显示错误消息。
这是我实施的解决方案。它实际上使用了 OnAuthorization。
我的目标是获得一个简单的解决方案,使我能够像工厂授权代码一样装饰操作。我已经成功了。
归功于
How do I get the MethodInfo of an action, given action, controller and area names? 感谢 Miguel Angelo。
和
jQuery Ajax error handling, show custom exception messages
信用 AlexMAS
如果不是这些人,永远不会想出这个。
为了安全起见,我正在使用 RBAC。在这里找到它。 https://www.codeproject.com/articles/1079552/custom-roles-based-access-control-rbac-in-asp-ne
出色的基于角色的安全性。好的系统。它通过 ASP.NET 身份框架扩展了基于表单的身份验证。
因此,如果您可以在控制器外部看到 IPrincipal.User,这会很简单,但我发现我无法将其传递给控制器中的方法,但仍然看到用于 RBAC 的扩展该方法中的权限。
但是你可以在这里看到它。
public class RBACAttribute:AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
do stuff.
}
}
所以技巧变成了如何正确填充 AuthorizationContext filterContext 然后我可以调用 OnAuthorize。
这是 Miguel 代码的用武之地。它是对控制器的扩展。我稍微改变了它,因为它实际上会从传入的控制器引用中获取所有信息。我只想要 ActionDescriptor 所以我可以填充 AuthorizationContext 对象
public static class GetControllerAttr
{
public static ActionDescriptor GetActionAttributes(this Controller @this,string action,string controller,string area,string method)
{
var actionName = action ?? @this.RouteData.GetRequiredString("action");
var controllerName = controller ?? @this.RouteData.GetRequiredString("controller");
var areaName = area ?? @this.RouteData.Values [ "area" ] ;
var methodName = method ?? @this.RouteData.GetRequiredString("action");
var controllerFactory = ControllerBuilder.Current.GetControllerFactory();
var controllerContext = @this.ControllerContext;
var otherController = (ControllerBase)controllerFactory
.CreateController(
new RequestContext(controllerContext.HttpContext,new RouteData()),
controllerName);
var controllerDescriptor = new ReflectedControllerDescriptor(
otherController.GetType());
var controllerContext2 = new ControllerContext(
new MockHttpContextWrapper(
controllerContext.HttpContext.ApplicationInstance.Context,
methodName),
new RouteData(),
otherController);
var actionDescriptor = controllerDescriptor
.FindAction(controllerContext2,actionName);
return actionDescriptor ;
//var attributes = actionDescriptor.GetCustomAttributes(true)
// .Cast<Attribute>()
// .ToArray();
//return attributes;
}
}
class MockHttpContextWrapper:HttpContextWrapper
{
public MockHttpContextWrapper(HttpContext httpContext,string method)
: base(httpContext)
{
this.request = new MockHttpRequestWrapper(httpContext.Request,method);
}
private readonly HttpRequestBase request;
public override HttpRequestBase Request
{
get { return request; }
}
class MockHttpRequestWrapper:HttpRequestWrapper
{
public MockHttpRequestWrapper(HttpRequest httpRequest,string httpMethod)
: base(httpRequest)
{
this.httpMethod = httpMethod;
}
private readonly string httpMethod;
public override string HttpMethod
{
get { return httpMethod; }
}
}
}
我把Alex的代码稍微修改了一下,得到了我想发回给JQuery
的信息 public class ClientErrorHandler:FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
var response = filterContext.RequestContext.HttpContext.Response;
clsAuthorizationError _clsAuthorization = new clsAuthorizationError();
if(filterContext.Exception.Data.Contains("ErrorCode"))
{
_clsAuthorization.ErrorCode = (int)filterContext.Exception.Data["ErrorCode"];
_clsAuthorization.ReDirect = filterContext.Exception.Message;
string _results = JsonConvert.SerializeObject(_clsAuthorization);
response.Write(_results);
}
else
{
response.Write(filterContext.Exception.Message);
}
response.ContentType = MediaTypeNames.Text.Plain;
filterContext.ExceptionHandled = true;
}
}
public class clsAuthorizationError
{
public int ErrorCode { set; get; }
public string ReDirect { set; get; }
}
在覆盖的 OnAuthorization 方法中,我添加了 Url 字符串和错误代码。
public class RBACAttribute:AuthorizeAttribute
{
public string Url { set; get; }
public int ErrorCode { set; get; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
Url = null;
string _action = null;
string _controller = null;
try
{
if(!filterContext.HttpContext.Request.IsAuthenticated)
{
//Redirect user to login page if not yet authenticated. This is a protected resource!
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary { { "action",_action },
{ "controller",_controller },
{ "Area",String.Empty } });
Url = "/__controller__/__action__/";
Url = Url.Replace("__controller__",_controller);
Url = Url.Replace("__action__",_action);
ErrorCode = 401;
}
else
{
//Create permission string based on the requested controller name and action name in the format 'controllername-action'
string requiredPermission = String.Format("0}-{1}",
filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
filterContext.ActionDescriptor.ActionName);
if(!filterContext.HttpContext.User.HasPermission(requiredPermission) & !filterContext.HttpContext.User.IsSysAdmin())
{
_action = "Index";
_controller = "Unauthorised";
//User doesn't have the required permission and is not a SysAdmin, return our custom “401 Unauthorized” access error
//Since we are setting filterContext.Result to contain an ActionResult page, the controller's action will not be run.
//The custom “401 Unauthorized” access error will be returned to the browser in response to the initial request.
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary { { "action",_action },
{ "controller",_controller },
{ "Area",String.Empty } });
Url = "/__controller__/__action__/";
Url = Url.Replace("__controller__",_controller);
Url = Url.Replace("__action__",_action);
ErrorCode = 401;
}
//If the user has the permission to run the controller's action, the filterContext.Result will be uninitialized and
//executing the controller's action is dependant on whether filterContext.Result is uninitialized.
}
}
catch(Exception ex)
{
_action ="Error";
_controller = "Unauthorised";
Url = "/__controller__/__action__/";
Url = Url.Replace("__controller__",_controller);
Url = Url.Replace("__action__",_action);
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new { controller = _controller,action = _action,
_errorMsg = ex.Message })
ErrorCode = 500;
}
}
}
在 Ajax 调用中添加以下内容。
complete: function (jqXHR, textStatus, errorThrown)
{
var jparse = JSON.parse(jqXHR.responseText);
if (jparse.hasOwnProperty('ErrorCode'))
{
var code = jparse.ErrorCode;
var href = jparse.ReDirect;
window.location.href = href;
}
}
然后我创建了一个前端来组合 pc 的
public class clsOnAuthorization {
//private string Redirect { set; get; }
//string _Action { set; get; }
//string _Controller { set; get; }
//string _url { set; get; }
AuthorizationContext _filterContext;
public clsOnAuthorization(Controller @this)
{
_filterContext = new AuthorizationContext(@this.ControllerContext,GetControllerAttr.GetActionAttributes(@this,null,null,null,null));
Verify ( ) ;
}
public void Verify()
{
RBACAttribute _rbacAttribute = new RBACAttribute();
_rbacAttribute.OnAuthorization(_filterContext);
if(_rbacAttribute.Url != null)
{
Exception myEx = new Exception(_rbacAttribute.Url);
myEx.Data.Add("ErrorCode", _rbacAttribute.ErrorCode);
throw myEx;
}
}
}
最后我装饰了动作,在动作中打了一个电话
[ClientErrorHandler]
public string JobGuid()
{
// send controller in with constructor.
clsOnAuthorization _clsOnAuthorization = new clsOnAuthorization(this);
// if authorization fails it raises and exception and never comes back here.
some stuff if authorization good.
}
通过一个修饰和一个 class 实例化,所有授权问题都消失了,我的 ajax 调用现在知道出了什么问题,可以适当地重定向。