2016-12-27 2 views
2

Я создаю веб-приложение MVC, которое, по крайней мере, для частичной передачи данных, зависит от Ajax.Ajax Fails On Authorization

Действие контроллера

[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. Можно ли получить обратно информацию от сбоя авторизации в АЯКС ответ. Если да, то как?
  2. Если ответ на вопрос 1. нет, должен ли я проверять это разрешение до того, как я сделаю этот вызов?

Как всегда, любая помощь оценивается.

+0

[Авторизовать] вызывает только авторизацию. почему вам нужно добавить RBAC ?? –

+0

@Rudresh - Я использую модель контроля оценки на основе роли, которую я нашел здесь. https://www.codeproject.com/articles/1079552/custom-roles-based-access-control-rbac-in-asp-ne. RBAC фактически вызывает вызов OnAuthorization, который дает перенаправление. – BrownPony

ответ

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.

Это код кода Мигеля. Это расширение для контроллера. Я изменил его немного, потому что это будет на самом деле получить все его информации из опорного контроллера, который передается. Я только хочу, чтобы 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; } 
      } 
     } 
    } 

Я взял код Алекса изменил его немного, чтобы получить информация, которую я хотел послать обратно в 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. 
} 

С одним украшением и одного класса конкретизации всех проблем авторизации ушел и мои Аякса звонки теперь знаю, что пошло не так, и может перенаправлять соответствующим образом.

1

Похоже, вы используете MVC, а не Web API, веб-API должен предоставить вам хорошее сообщение JSON по умолчанию.

Одним из вариантов было бы проверить код состояния ответа, это должно дать вам 401, если это ошибка аутентификации.

Еще бы убрать [Авторизовать] и сделать проверку внутри самого метода

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

Примечание: Я уверен, что есть любитель способ сделать это, но это должно работать

+0

Вверх проголосовали за отправку меня в направлении, которое привело к решению. – BrownPony

0

Используйте другой параметр complete: точно так же, как success: и error: для проверки отказа авторизации в вашем вызове $.ajax(). После success реализовать этот кусок кода. здесь код показывает ошибку авторизации. проверка состояния.

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 вызов завершен.

+0

Это статус статуса = 200 успешных! – BrownPony

+0

Кажется, что всегда возвращается 200, если вызов Ajax завершается и не имеет сбой при общении с сервером. Ошибка 401 системы авторизации C# не является ошибкой для данного устройства. – BrownPony

+0

PLZ проверить это руководство в случае, если вы делаете что-то другое, вы увидите, как вызов ajax возвращает ошибку 401 в полном разделе https://www.youtube.com/watch?v=UkrIS-xUMkA&list=PL6n9fhu94yhW7yoUOGNOfHurUE6bpOO2b&index=19 – Rajput