ASP.net роли и проекты - PullRequest
       28

ASP.net роли и проекты

5 голосов
/ 25 февраля 2010

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


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

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

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

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

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

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

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

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

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

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

А как насчет пользователей ASP.Net?
В системе будет много пользователей ASP.Net, давайте сосредоточимся на пользователях клиента:

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

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

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

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

Обобщенная:

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

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

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

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

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

Ответы [ 4 ]

3 голосов
/ 28 февраля 2010

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

1 голос
/ 28 февраля 2010

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

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


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

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

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

Существенные грани предлагаемого решения:

  • Общая база данных и строка подключения,
  • Общее имя участника программы,
  • Общий раздел machineKey, так что каждый сайт будет использовать тикет общих форм.
  • A UNIQUE имя приложения поставщика ролей (или, как вы говорите, 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 голос
/ 28 февраля 2010

Это именно тот сценарий, который требует пользовательского 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 (более или менее):

(ПРИМЕЧАНИЕ: ваш класс должен наследовать от 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

И вот ссылки, которые я обещал:

0 голосов
/ 25 февраля 2010

Сохранить значение в профиле потенциально. Установите запись профиля в файле конфигурации и используйте ее для хранения значения.

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...