Роли ASP.net и Проекты

РЕДАКТИРОВАНИЕ - Переписало мой исходный вопрос дать немного больше информации


Вводная информация
На моей работе я работаю над веб-приложением ASP.NET для наших клиентов. В нашей реализации мы используем технологии как аутентификация Форм с MembershipProviders и RoleProviders. Все подходили, пока я не столкнулся с некоторыми трудностями с конфигурированием ролей, потому что роли не в масштабе всей системы, но связанные с клиентскими счетами и проектами.

Я не могу назвать нашу точную установку/формулу, потому что я думаю, что наша компания не утвердила бы это...

Кто такой клиент / проект?
Наша компания предоставляет информацию управления нашим клиентам на ежегодном (или другой интервал) основание.
В наших системах клиент/контракт состоит из:

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

Установка сайта экстранет
В конечном счете мы хотим, чтобы все клиенты смогли получить доступ к своей информации управления с нашей системой онлайн. Экстранет состоит из двух сайтов:

  • Сайт компании: предоставляет обзор Сведений об учетной записи и продуктов
  • Измерительный сайт: после выбора Измерения, подробной информации о том промежутке времени

Измерительный сайт является самой интересной частью экстранет. Мы создадим подмодули для новых обзоров, отчетов, справляясь и поддерживая ресурсы, которые важны для исследования.

Наше решение для Visual Studio состоит из многих проектов. Одно веб-приложение под названием Портал для основания. Сайты и модули являются виртуальными каталогами в рамках того приложения (помогает совместно использовать MasterPages среди вещей).

Какие роли?
Читающие пользователи (чтение: роли), будет использовать систему:

  • Администраторы: пользователи разработки :) (не связанный клиент, полный доступ)
  • Сотрудники: сотрудники нашей компании (не связанный клиент, полный доступ)
  • Клиент SuperUser: менеджеры по верхнему уровню (полный доступ к их учетной записи/измерению)
  • Клиент ContactPerson: основной контакт (полный доступ к их измерению (измерениям))
  • Менеджер по работе с клиентами: начальник отдела (ограниченный доступ, определенные данные измерения)

Что относительно пользователей ASP.NET?
Система будет иметь многих пользователей ASP.NET, давайте сфокусируемся на клиентских пользователях:

  • Пользователи не совместно используются Учетными записями
  • SuperUser X автоматически имеет доступ ко всем (и новый) измерения
  • Пользователь Y мог быть Основным контактом для Измерения 1, но не иметь никакой роли для Измерения 2
  • Пользователь Y мог быть Основным контактом для Измерения 1, но иметь роль менеджера для Измерения 2
  • Начальники отделов являются многими отдельными пользователями (на Измерение), если бы у менеджера Z был вход в систему для Измерения 1, то мы хотели бы использовать тот вход в систему снова, если он участвует в Измерении 2.

Структура URL
Это типичные URL в нашем приложении:

Мы также создадим URL документа, где можно запросить определенный документ, он - GUID. Система должна будет проверить, имеет ли пользователь права на документ. Документ связан с Измерением, Пользователь или определенные роли имеют определенные права на документ.

В чем проблема? (наконец ;))
Ролей не достаточно для определения то, что пользователю разрешают видеть/получать доступ/загружать определенный объект. Недостаточно сказать, что определенный элемент навигации доступен для менеджеров. Когда пользователь запрашивает Измерение 1000, мы должны проверить, что у пользователя не только есть роль менеджера, но и роль менеджера для Измерения 1000.

Полученный в итоге:

  1. Как мы можем ограничить пользователей их учетными записями/измерениями?
    (помните, что суперпользователи видят все измерения, некоторые менеджеры только определенные измерения),

  2. Как мы можем применить роли на уровне продукта/измерения? (пользователь X мог быть primarycontact для измерения 1, но просто менеджер для измерения 2),

  3. Как мы можем ограничить доступ менеджера к экрану отчетов и только к отчетам их отдела?

Все с волшебством классов asp.net, возможно, с пользовательской roleprovider реализацией.

Подобный вопрос/проблема Stackoverflow
ASP.NET, как управлять пользователями с различными типами ролей

5
задан Community 23 May 2017 в 10:27
поделиться

4 ответа

В различных сообщениях, которые я вижу, вы ищете механизм настраиваемых ролей или, иначе говоря, настраиваемый механизм авторизации. Для аутентификации по-прежнему можно использовать стандартный SqlMembershipProvider.

Я не уверен, что стандартный поставщик ролей предоставит вам то, что вы хотите, поскольку для авторизации требуется наличие контекста проекта. Однако вы можете исследовать написание настраиваемого RoleProvider, чтобы узнать, можете ли вы создать некоторые настраиваемые методы, которые будут делать это. Тем не менее, чтобы ответить на этот вопрос, я предполагаю, что вы не можете использовать SqlRoleProvider.

Итак, вот некоторая потенциальная схема:

Create Table Companies
(
    Id int not null Primary Key
    , ...
)
Create Table Projects
(
    Id int not null Primary Key
    , PrimaryContactUserId uniqueidentifier
    , ...
    , Constraint FK_Projects_aspnet_Users
        Foreign Key ( PrimaryContactUserId )
        References dbo.aspnet_Users ( UserId )
)
Create Table Roles
(
    Name nvarchar(100) not null Primary Key
    , ...
)

Create Table ProjectCompanyRoles
(
    CompanyId int not null
    , ProjectId int not null
    , RoleName nvarchar(100) not null
    , Constraint FK_...
)

Как я сказал ранее, причина включения PrimaryContact в таблицу Projects состоит в том, чтобы гарантировать, что для данного проекта существует только одна схема. Если вы включите его в качестве роли, вам придется включить кучу кода прыжка через обруч, чтобы гарантировать, что проекту не назначено более одного PrimaryContact. Если это так, возьмите PrimaryContactUserId из таблицы Projects и сделайте его ролью.

Проверки авторизации повлекут за собой запросы к ProjectCompanyRoles. Опять же, добавление контекстов Project и Company затрудняет использование поставщиков ролей по умолчанию. Если вы хотите использовать.NET для ролей, а также для аутентификации, тогда вам придется реализовать свой собственный RoleProvider.

3
ответ дан 14 December 2019 в 19:11
поделиться

Возможно, сохранить значение в профиле. Настройте запись профиля в файле конфигурации и используйте ее для хранения значения.

Более реалистично, вы можете захотеть сохранить это вне таблиц ASP.NET для простоты использования и облегчения доступа к значению (возможно, за пределами веб-среды, если вам нужно) ...

Не уверен каковы все ваши требования.

0
ответ дан 14 December 2019 в 19:11
поделиться

Это именно тот сценарий, в котором требуется настраиваемый RoleProvider. Вы разрабатываете схему базы данных для поддержки своего случая (вы можете создать таблицу с именем ProjectRole и таблицу с именем CompanyRole).

Вот пара вещей, с которых вы можете начать (со ссылками на помощь внизу):

Добавьте этот раздел в свой web.config:

<roleManager defaultProvider="MyRoleProvider" enabled="true">
    <providers>
        <add name="MyRoleProvider" type="MyNamespace.MyRoleProvider, MyAssembly, Version=1.0.0.0" description="My Custom Role Provider." enableSearchMethods="false" applicationName="MyApplicationName"/>
    </providers>
</roleManager>

Тогда это то, что MyRoleProvider class выглядит (более или менее):

(ПРИМЕЧАНИЕ: ваш класс должен унаследовать от System.Web.Security.RoleProvider )

namespace MyNamespace
{
    ...

    public class MyRoleProvider : System.Web.Security.RoleProvider
    {
        private string _applicationName;

        public MyRoleProvider()
        {
        }

        public override string ApplicationName
        {
            get
            {
                return _applicationName;
            }
            set
            {
                _applicationName = value;
            }
        }

        ...

    }
}

Затем вам просто нужно переопределить некоторые методы, чтобы предоставить вашему приложению необходимая информация:

Как минимум, я бы переопределил эти 2 метода:

  • GetRolesForUser
  • IsUserInRole

Но вы также можете переопределить эти методы, если хотите:

  • AddUsersToRoles
  • RemoveUsersFromRoles
  • FindUsersInRole
  • GetUsersInRole
  • GetAllRoles
  • CreateRole
  • DeleteRole
  • RoleExists

Здесь также нет обещанных мной ссылок:

1
ответ дан 14 December 2019 в 19:11
поделиться

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: В соответствии с обменом в комментариях, в котором я полностью себя представляю, почти готовое решение был получен, и этот ответ был очищен от всяких сомнений и теперь содержит только протестированный сценарий, который может или не может решить проблему OP. ;-)

Престижность Томасу за то, что он сохранил хладнокровие и не сдался.


Z- скажите мне, понимаю ли я вас:

Вам нужен центральный поставщик членства для всех приложений / проектов и отдельный бункер ролей для каждого приложения / проекта?

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

Основные аспекты предлагаемого решения:

  • Общая база данных и строка подключения,
  • Общее имя приложения членства,
  • Общий раздел machineKey, чтобы каждый сайт использовал билет общих форм.
  • УНИКАЛЬНОЕ имя приложения поставщика ролей (или projectId, как вы говорите).
  • Измененный aspnet_Users_DeleteUser sproc.

Модификация aspnet_Users_DeleteUser включает в себя очистку пользовательских ссылок в aspnet_users, которые динамически создаются поставщиками ролей и профилей, и несет условие, что конкретный экземпляр aspnet_db принадлежит общему MembershipProvider, и только сайты, которые используют этого общего поставщика членства, должны подключаться к нему.

Чтобы сопоставить это решение со сценарием OP:

Каждая учетная запись / компания будет иметь отдельный экземпляр aspnet_db, а «ProjectId» будет сопоставлен с атрибутом applicationName элемента провайдера RoleManager.

Поскольку проекты «переносятся», им назначается новый ProjectId (applicationName), и при этом пользователи компаний могут аутентифицироваться в перенесенном проекте с помощью общего поставщика членства, но роли из исходного проекта не переносятся. в силу различных поставщиков ролей.

Все стандартные стратегии управления членством, например Инструмент настройки AspNet, элементы управления входом, мастера создания пользователей, функции членства (особенно Membership.DeleteUser () - спасибо, Томас) будут вести себя так, как ожидалось, без каких-либо изменений.

Профили могут быть реализованы в любом направлении, использование applicationId поставщика членства позволит данным профиля следовать за пользователем в любой из связанных проектов. Использование отдельного ProjectId (applicationName) поставщика ролей позволит создать отдельные профили для каждого пользователя в каждом проекте.

Некоторые подробности и тесты здесь .

Основные разделы конфигурации перечислены ниже, а за ними следует модифицированная sproc.

Web.config

<?xml version="1.0"?>
<configuration>
  <connectionStrings>
    <add name="testDb" providerName="System.Data.SqlClient" connectionString="Data Source=(local);Initial Catalog=__SingleAuthMultiRole;Integrated Security=True"/>
  </connectionStrings>
  <system.web>
    <compilation debug="true"/>

    <!-- this key is common all your apps - generate a new one @ http://www.developmentnow.com/articles/machinekey_generator.aspx -->
    <machineKey validationKey="841FEF8E55CD7963CE9EAFED329724667D62F4412F635815DFDDBE7D2D8D15819AE0FDF70CEF8F72792DBD7BF661F163B01134092CBCB80D7D71EAA42DFBF0A9" decryptionKey="FC9B0626224B0CF0DA68C558577F3E37723BB09AACE795498C4069A490690669" validation="SHA1" decryption="AES"/>

    <authorization>
      <deny users="?"/>
    </authorization>

    <authentication mode="Forms" />

    <membership defaultProvider="SqlProvider" userIsOnlineTimeWindow="15">
      <providers>
        <clear/>
        <add name="SqlProvider"
             type="System.Web.Security.SqlMembershipProvider"
             connectionStringName="testDb"
             applicationName="Common"  /> <!-- membership applicationName is common to all projects  -->
      </providers>
    </membership>

    <roleManager enabled="true" defaultProvider="SqlRoleManager" cacheRolesInCookie="true">
      <providers>
        <add name="SqlRoleManager"
             type="System.Web.Security.SqlRoleProvider"
             connectionStringName="testDb"
             applicationName="WebApplication1"/> <!-- roleManager applicationName is unique to each projects  -->
      </providers>
    </roleManager>

  </system.web>
</configuration>

Использование : После подготовки Aspnet_db с помощью aspnet_regsql.exe запустите этот сценарий, чтобы изменить sproc aspnet_Users_DeleteUser.

/*************************************************************/
/*************************************************************/
--- Modified DeleteUser SP

IF (EXISTS (SELECT name
              FROM sysobjects
             WHERE (name = N'aspnet_Users_DeleteUser')
               AND (type = 'P')))
DROP PROCEDURE [dbo].aspnet_Users_DeleteUser
GO
CREATE PROCEDURE [dbo].[aspnet_Users_DeleteUser]
    @ApplicationName  nvarchar(256),
    @UserName         nvarchar(256),
    @TablesToDeleteFrom int,
    @NumTablesDeletedFrom int OUTPUT    

AS
BEGIN
    -- holds all user id for username
    DECLARE @UserIds TABLE(UserId UNIQUEIDENTIFIER)
    SELECT  @NumTablesDeletedFrom = 0

    DECLARE @TranStarted   bit
    SET @TranStarted = 0

    IF( @@TRANCOUNT = 0 )
    BEGIN
        BEGIN TRANSACTION
        SET @TranStarted = 1
    END
    ELSE
    SET @TranStarted = 0

    DECLARE @ErrorCode   int
    DECLARE @RowCount    int

    SET @ErrorCode = 0
    SET @RowCount  = 0

    -- get all userid for username
    INSERT INTO @UserIds
    SELECT  UserId
    FROM    dbo.aspnet_Users 
    WHERE   LoweredUserName = LOWER(@UserName)

DECLARE @tmp int
SELECT @tmp = COUNT(*) FROM @UserIds
    IF NOT EXISTS(SELECT * FROM @UserIds)
        GOTO Cleanup

    -- Delete from Membership table if (@TablesToDeleteFrom & 1) is set
    IF ((@TablesToDeleteFrom & 1) <> 0 AND
        (EXISTS (SELECT name FROM sysobjects WHERE (name = N'vw_aspnet_MembershipUsers') AND (type = 'V'))))
    BEGIN
        DELETE FROM dbo.aspnet_Membership WHERE UserId IN (SELECT UserId from @UserIds)

        SELECT @ErrorCode = @@ERROR,
               @RowCount = @@ROWCOUNT

        IF( @ErrorCode <> 0 )
            GOTO Cleanup

        IF (@RowCount <> 0)
            SELECT  @NumTablesDeletedFrom = @NumTablesDeletedFrom + 1
    END

    -- Delete from aspnet_UsersInRoles table if (@TablesToDeleteFrom & 2) is set
    IF ((@TablesToDeleteFrom & 2) <> 0  AND
        (EXISTS (SELECT name FROM sysobjects WHERE (name = N'vw_aspnet_UsersInRoles') AND (type = 'V'))) )
    BEGIN
        DELETE FROM dbo.aspnet_UsersInRoles WHERE UserId IN (SELECT UserId from @UserIds)

        SELECT @ErrorCode = @@ERROR,
                @RowCount = @@ROWCOUNT

        IF( @ErrorCode <> 0 )
            GOTO Cleanup

        IF (@RowCount <> 0)
            SELECT  @NumTablesDeletedFrom = @NumTablesDeletedFrom + 1
    END

    -- Delete from aspnet_Profile table if (@TablesToDeleteFrom & 4) is set
    IF ((@TablesToDeleteFrom & 4) <> 0  AND
        (EXISTS (SELECT name FROM sysobjects WHERE (name = N'vw_aspnet_Profiles') AND (type = 'V'))) )
    BEGIN
        DELETE FROM dbo.aspnet_Profile WHERE UserId IN (SELECT UserId from @UserIds)

        SELECT @ErrorCode = @@ERROR,
                @RowCount = @@ROWCOUNT

        IF( @ErrorCode <> 0 )
            GOTO Cleanup

        IF (@RowCount <> 0)
            SELECT  @NumTablesDeletedFrom = @NumTablesDeletedFrom + 1
    END

    -- Delete from aspnet_PersonalizationPerUser table if (@TablesToDeleteFrom & 8) is set
    IF ((@TablesToDeleteFrom & 8) <> 0  AND
        (EXISTS (SELECT name FROM sysobjects WHERE (name = N'vw_aspnet_WebPartState_User') AND (type = 'V'))) )
    BEGIN
        DELETE FROM dbo.aspnet_PersonalizationPerUser WHERE UserId IN (SELECT UserId from @UserIds)

        SELECT @ErrorCode = @@ERROR,
                @RowCount = @@ROWCOUNT

        IF( @ErrorCode <> 0 )
            GOTO Cleanup

        IF (@RowCount <> 0)
            SELECT  @NumTablesDeletedFrom = @NumTablesDeletedFrom + 1
    END

    -- Delete from aspnet_Users table if (@TablesToDeleteFrom & 1,2,4 & 8) are all set
    IF ((@TablesToDeleteFrom & 1) <> 0 AND
        (@TablesToDeleteFrom & 2) <> 0 AND
        (@TablesToDeleteFrom & 4) <> 0 AND
        (@TablesToDeleteFrom & 8) <> 0 AND
        (EXISTS (SELECT UserId FROM dbo.aspnet_Users WHERE UserId IN (SELECT UserId from @UserIds))))
    BEGIN
        DELETE FROM dbo.aspnet_Users WHERE UserId IN (SELECT UserId from @UserIds)

        SELECT @ErrorCode = @@ERROR,
                @RowCount = @@ROWCOUNT

        IF( @ErrorCode <> 0 )
            GOTO Cleanup

        IF (@RowCount <> 0)
            SELECT  @NumTablesDeletedFrom = @NumTablesDeletedFrom + 1
    END

    IF( @TranStarted = 1 )
    BEGIN
        SET @TranStarted = 0
        COMMIT TRANSACTION
    END

    RETURN 0

Cleanup:
    SET @NumTablesDeletedFrom = 0

    IF( @TranStarted = 1 )
    BEGIN
        SET @TranStarted = 0
        ROLLBACK TRANSACTION
    END

    RETURN @ErrorCode

END
GO
1
ответ дан 14 December 2019 в 19:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: