1

Я не уверен, придумал ли я термин, приведенный выше в заголовке, но ниже - мое резюме проблемы.Авторизация с идентификатором ASP.NET

Я разработал API REST с использованием ASP.NET WebAPI с идентификацией для аутентификации.

Для примера. например, у меня есть API компании, лежащий на http://<myurl>/api/Companies для GET и POST и http://<myurl>/api/Companies/{id} для GETBYID, PUT и DELETE.

При тестировании API я обнаружил, что после аутентификации пользователя (по токенам) он/она может редактировать любую компанию.

Я бы хотел ограничить пользователей редактированием/удалением компаний (и других объектов), к которым он принадлежит (в соответствии с логической структурой таблицы).

Solution On My Mind: Первая мысль приходит на ум использует ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());, чтобы найти пользователь, который запрашивающую операцию, а затем найти компанию он/принадлежит сказать по user.CompanyID и матчу с, если s Полезной Нагрузки/он пытается отредактировать/удалить или выполнить некоторую другую операцию на том же объекте, к которому он принадлежит.

Да, для разных лиц это будет user.EntityID. Но правильно ли это сделать?

Потому что таким образом, я должен написать этот код на всех моих контроллерах Edit and Delete operation.

Я знаю, ASP.NET не может ограничивать вещи в логической структуре. Например, здесь, в моем случае, он не может определить, пытается ли пользователь отредактировать компанию, к которой он принадлежит или нет, поскольку это что-то логичное для отношения к БД. Но есть ли что-то вроде атрибута [Authorize], который я могу разработать, который будет содержать вышеуказанную логику, а не загромождать контроллер?

ОБНОВЛЕНИЕ: Не уверен, что все понимают ситуацию, но вот еще одна история, поясняющая. Это приложение SaaS в облаке с базой данных Multi-Tenant. Таким образом, у каждой Компании есть свой набор Пользователей и других Субъектов, которые не могут иметь доступ к Объектам за пределами своей Компании (в соответствии с табличным отношением).

API-интерфейсы обрабатываются Front-End WebApp, поэтому Ограничение по показам что я делаю сейчас. Но меня беспокоит, что, если кто-то просто проверяет JS-файл и вызывает API напрямую?

Мне нужно что-то, чтобы ограничить пользователя только его сущностями.

ответ

2

Там есть много способов разрезать это, но я думаю, что наиболее безопасным было бы установить SQL Views, которые позволят вашим пользователям получать доступ к данным из основных таблиц, фильтруя данные на основе пользователя/организации/ролей/претензий.Если вы не знакомы с представлениями, вы в основном создаете свою базовую структуру таблиц, тогда представления создаются как слой над таблицами, каждый из которых генерируется с помощью запроса, тогда ваше приложение верхнего уровня может просто запрашивать представления.

Вы можете создать пользовательский класс AuthorizationFilterAttribute, чтобы применить дополнительные фильтры декларативно (как работает [Authorize]). Следующий даст вам [AuthorizeActive] фильтр, который можно применить к действиям, которые будут проверять на пользователь ID в базу данных, чтобы гарантировать, что они active по каждому запросу:

using System; 
using System.Net; 
using System.Net.Http; 
using System.Security.Claims; 
using System.Web.Http; 
using System.Web.Http.Controllers; 
using System.Web.Http.Filters; 
using Microsoft.AspNet.Identity; 


public class AuthorizeActiveAttribute : AuthorizationFilterAttribute 
{ 
    private readonly IUserService _userService; 
    public AuthorizeActiveAttribute() { 
     _userService = Injection.Get<IUserService>(); 
    } 

    public override void OnAuthorization(HttpActionContext filterContext) { 
     if (filterContext == null) 
      throw new ArgumentNullException(nameof(filterContext)); 

     if (SkipAuthorization(filterContext)) return; 
     var userId = ClaimsPrincipal.Current?.Identity?.GetUserId<int>(); 
     if (userId != null && userId > 0 && _userService.GetUserStatus((int) userId)) { 
      base.OnAuthorization(filterContext); 
     } 
     else { 
      filterContext.Response = filterContext.Request.CreateResponse(HttpStatusCode.Unauthorized); 
     } 
    } 


    private static bool SkipAuthorization(HttpActionContext filterContext) { 
     var hasAnonymousAction = filterContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0; 
     var hasAnonymousController = filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0; 
     return hasAnonymousAction || hasAnonymousController; 
    } 
} 

Кроме того, вы можете установить контроллер базовой апи, содержащий общие логика для авторизации (возможно, вы хотите, чтобы все интерфейсы, чтобы быть разрешены по умолчанию):

/// <summary> 
/// Base controller for resource related API calls. 
/// </summary> 
[AuthorizeActive] 
public abstract class ResourceController : ApiController { 
    public bool IsAuthorized => User?.Identity?.IsAuthenticated == true; 
    public int ActualUserId => GetIntegerClaimValue("actualuserid"); 
    public int UserId => GetIntegerClaimValue("userid"); 
    public int UserTypeId => GetIntegerClaimValue("usertypeid"); 
    public UserTypes UserType => (UserTypes)UserTypeId; 
    public int ActualOrganizationId => GetIntegerClaimValue("actualorganizationid"); 
    public int OrganizationId => GetIntegerClaimValue("organizationid"); 
    public string Fingerprint => GetClaimValue("fingerprint"); 

    protected string GetClaimValue(string shortName) => (User?.Identity as ClaimsIdentity)?.Claims?.GetValue($"http://schemas.example.com/identity/claims/{shortName}"); 
    protected int GetIntegerClaimValue(string shortName) => Convert.ToInt32(GetClaimValue(shortName)); 
} 

public HomeController : ResourceController { 
} 

Этого пример тягового пользовательские требования из идентификатора пользователя .NET, которые соответствуют организациям пользователей, являются ли они олицетворением, и т.д.

Теперь все ваши вызовы API разрешены и защищены, чтобы разрешить пользователям, которые активны в вашей базе данных по умолчанию, разрешить доступ анонимно, украсить метод [AllowAnonymous]. В пределах ваших контроллеров, которые наследуют ResourceController, у вас всегда будет доступ к любому из его защищенных свойств, что упростит захват идентификатора организации пользователей и использование его в ваших запросах.

+0

Это потребует от системы расширения системы identity/auth до уровня базы данных, что может считаться плохим дизайном. – Dai

+0

Кроме того, во многих системах баз данных (таких как те, которые не могут интегрироваться со службой Directory, например Neo4j) это не является возможным решением, а интеграция с предприятиями сложна во многих базах данных - и это невозможно в общедоступные сценарии веб-приложений. – Dai

+0

Это на самом деле то, как Microsoft самостоятельно защитила безопасность для [Microsoft Dynamics CRM] (https://technet.microsoft.com/en-us/library/dn531182.aspx). Хотя это может быть более масштабное мероприятие, я сомневаюсь, что многие согласятся с тем, что блокирование базы данных на уровне базы данных является «плохим дизайном». Напротив, он защищен от лазеек безопасности. – cchamberlain

1

Правильные, пользовательские правила доступа должны быть реализованы как часть вашей бизнес-логики, однако вы можете упростить некоторые вещи.

Например, если некоторые из ваших лиц все имеют CompanyId свойство, которое указывает, что они принадлежат к той или иной компании, то вы могли бы определить IHasCompanyOwner и затем один метод проверки доступа:

interface IHasCompanyOwner { 
    Int32 CompanyId { get; } 
} 


public partial class Widget : IHasCompanyOwner {} 
public partial class Shop : IHasCompanyOwner {} 

private Boolean CanAccessCompanyEntity(IHasCompanyOwner entity) {P 
    return entity.CompanyId == ((MyUserIdentity)this.User.Identity).CompanyId; 
} 

public ActionResult GetWidget(Int32 widgetId) { 

    Widget w = this.database.GetWidget(widgetId); 
    if(!CanAccessCompanyEntity(w)) return this.Http403("You don't have permission to access this Widget"); 
} 

public ActionResult GetShop(Int32 shopId) { 

    Shop s = this.database.GetShop(shopId); 
    if(!CanAccessCompanyEntity(s)) return this.Http403("You don't have permission to access this Shop"); 
} 
+0

Это кажется разумным. –

Смежные вопросы