【问题标题】:Ajax Fails On AuthorizationAjax 授权失败
【发布时间】:2016-12-27 19:04:14
【问题描述】:

我正在构建 MVC Web 应用程序,它的至少部分数据传输依赖于 Ajax。

控制器动作是

[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 上什么也得不到。没有什么可以告诉我存在授权错误。

问题:

  1. 是否可以将授权失败的信息返回到 ajax 响应中。如果有怎么办?
  2. 如果对 1. 的回答是否定的,我是否应该在拨打此电话之前检查此授权?

一如既往,感谢您的帮助。

【问题讨论】:

  • [Authorize] 只会触发授权。为什么需要添加 RBAC??
  • @Rudresh - 我正在使用我在这里找到的基于角色的评估控制模型。 codeproject.com/articles/1079552/…。 RBAC 实际上触发了对提供重定向的 OnAuthorization 的调用。

标签: jquery ajax asp.net-mvc authorize-attribute


【解决方案1】:

这是一个完整的解决方案,它允许您在操作中使用单个调用来装饰您的操作,其工作方式与 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 Identity 的框架扩展了基于表单的身份验证。

因此,如果您可以在控制器外部看到 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;
   }
}

然后我创建了一个前端来组装电脑的

公共类 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.
} 

通过一个装饰和一个类实例化,所有授权问题都消失了,我的 ajax 调用现在知道出了什么问题并且可以适当地重定向。

【讨论】:

    【解决方案2】:

    看起来您使用的是 MVC 而不是 Web API,默认情况下 Web API 应该会给您一个不错的 JSON 消息。

    一种选择是检查响应的状态码,如果是身份验证失败,这应该会给你一个 401。

    另一种方法是删除 [Authorize] 并在方法本身内部进行检查

    public string GetData(string inputdata)
    {
       if (User.Identity.IsAuthenticated) { 
          return  jsonData;
       } 
       return failureJson;
    }
    

    注意:我确信有一种更好的方法可以做到这一点,但这应该可行

    【讨论】:

    • 投了赞成票,让我朝着一个可以找到解决方案的方向前进。
    【解决方案3】:

    使用另一个参数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**
                        }
    }
    

    只要您的 ajax 调用完成,就会返回complete:。

    【讨论】:

    • 这是返回 status=200 成功!
    • 如果 Ajax 调用完成并且与服务器通信没有失败,它似乎总是返回 200。来自 c# 授权系统的 401 错误不是本单元的错误。
    • 请查看本教程以防万一您在做一些不同的事情,您将在完整部分中看到 ajax 调用如何返回 401 错误youtube.com/…
    • 我不会在每次通话时都发送密码和用户 ID。用户已登录系统。也许这就是区别。我所有的测试都表明,如果 AJAX 调用了某事,我将返回 200。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-03
    • 2021-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多