ASP.net角色和项目

时间:2010-02-25 12:25:31

标签: asp.net asp.net-membership forms-authentication roles

编辑 - 重写了我原来的问题以提供更多信息


背景信息
在我的工作中,我正在为我们的客户开发ASP.Net Web应用程序。在我们的实现中,我们使用诸如Forms身份验证与MembershipProviders和RoleProviders之类的技术。一切顺利,直到我遇到配置角色的一些困难,因为角色不是系统范围的,而是与客户帐户和项目相关。

我无法确定我们的确切设置/公式,因为我认为我们公司不会批准......

什么是客户/项目?
我们公司每年(或其他时间间隔)为客户提供管理信息 在我们的系统中,客户/合同包括:

  • 一个帐户:有关公司的信息
  • 每个帐户,一个或多个产品:我们将提供的管理信息包
  • 每件产品,一个或多个测量:一段时间,我们收集并报告数据

外联网网站设置
最终,我们希望所有客户都能够通过我们的在线系统访问他们的管理信息。外联网由两个站点组成:

  • 公司网站:提供帐户信息和产品概述
  • 测量地点:选择测量后,有关该时间段的详细信息

测量站点是外联网中最有趣的部分。我们将为新的概述,报告,管理和维护对研究重要的资源创建子模块。

我们的Visual Studio解决方案包含许多项目。一个名为Portal的Web应用程序作为基础。站点和模块是该应用程序中的虚拟目录(使得在事物之间共享MasterPages更容易)。

什么样的角色?
以下用户(读取:角色)将使用系统:

  • 管理员:开发用户:)(与客户无关,完全访问权限)
  • 员工:我们公司的员工(非客户相关,完全访问权限)
  • 客户超级用户:顶级经理(完全访问其帐户/度量)
  • 客户联系人:主要联系人(完全访问他们的度量)
  • 客户经理:部门经理(访问受限,测量的具体数据)

ASP.Net用户怎么样?
该系统将拥有许多ASP.Net用户,让我们关注客户用户:

  • 帐户
  • 之间不共享用户
  • SuperUser X可自动访问所有(和新的)测量值
  • 用户Y可以是Measurement 1的主要联系人,但对Measurement 2没有任何作用
  • 用户Y可以是Measurement 1的主要联系人,但具有Measurement 2的Manager角色
  • 部门经理是很多个人用户(每次测量),如果经理Z有测量1的登录,我们希望再次使用该登录,如果他参加测量2。

网址结构
这些是我们申请中的典型网址:

我们还将创建一个文档URL,您可以通过它的GUID请求特定文档。系统必须检查用户是否拥有该文档的权限。该文档与度量相关,用户或特定角色对文档具有特定权限。

有什么问题? (最后;))
角色不足以确定允许用户查看/访问/下载特定项目的内容。仅仅说管理者可以访问某个导航项是不够的。当用户请求Measurement 1000时,我们必须检查用户不仅具有Manager角色,还具有Measurement 1000的Manager角色。

总结:

  1. 我们如何限制用户的帐户/度量? (记住超级用户看到所有测量值,一些经理只能进行特定测量)

  2. 我们如何在产品/测量级别应用角色? (用户X可以是测量1的主要接触,但只是测量2的管理者)

  3. 我们如何限制管理员访问报告屏幕并仅限制其部门的报告?

  4. 所有这些都具有asp.net类的神奇功能,可能还有自定义的roleprovider实现。

    类似Stackoverflow问题/问题
    ASP.NET, how to manage users with different types of roles

4 个答案:

答案 0 :(得分:3)

您从我看到的各种帖子中寻求的是自定义角色机制或另一种方式,即自定义授权机制。身份验证仍然可以使用标准的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_...
)

正如我之前所说,在Projects表中包含PrimaryContact的原因是为了确保给定项目只有一个。如果将其作为角色包含在内,则必须包含一堆箍跳代码,以确保项目未分配多个PrimaryContact。如果是这种情况,那么从Projects表中取出PrimaryContactUserId并使其成为一个角色。

授权检查需要对ProjectCompanyRoles进​​行查询。同样,Project和Company的上下文的添加使得使用默认角色提供程序成为问题。如果您想使用.NET机制进行角色和身份验证,那么您必须实现自己的自定义RoleProvider。

答案 1 :(得分:1)

这正是需要自定义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;
            }
        }

        ...

    }
}

然后,您只需要覆盖一些方法,为您的应用程序提供所需的信息:

至少,我会覆盖这两种方法:

  • GetRolesForUser
  • 的isUserInRole

但是如果您想要:

,也可以覆盖这些方法
  • AddUsersToRoles
  • RemoveUsersFromRoles
  • FindUsersInRole
  • GetUsersInRole
  • GetAllRoles
  • CREATEROLE
  • DeleteRole
  • RoleExists

这里也没有我承诺的链接:

答案 2 :(得分:1)

免责声明:根据评论中的交换,我对自己做了一个完整的解决方案,一个几乎开箱即用的解决方案已经到达并且这个答案已被清除所有的捣蛋和现在只包含一个测试的场景,可能会或可能不会解决OP问题。 ;-)

感谢托马斯保持冷静而不放弃。


Z-告诉我,如果我理解你:

您想要所有应用/项目的中央会员提供商以及每个应用/项目的独特角色孤岛吗?

您可能不需要实现自定义提供程序。标准堆栈可以满足 次要存储过程修改 。最好尝试和甜言蜜语谈谈你想做的事情。它会减少工作量和睡眠时间。

拟议解决方案的突出方面:

  • 常用数据库和连接字符串
  • 常见的会员申请名称
  • 一个常见的machineKey部分,以便每个站点都使用公共表单票证。
  • UNIQUE 角色提供程序应用程序名称(或projectId,正如您所说)。
  • 修改后的aspnet_Users_DeleteUser sproc。

对aspnet_Users_DeleteUser的修改涉及清理由角色和配置文件提供程序动态创建的aspnet_users中的用户引用,并且包含公共MembershipProvider特定的aspnet_db实例拥有的条件,并且仅使用该公共成员资格提供者的网站应该连接到它。

将此解决方案映射到OP方案:

每个帐户/公司都有一个独特的aspnet_db实例,'ProjectId'将映射到RoleManager提供程序元素的applicationName属性。

当项目“迁移”时,会为它们分配一个新的ProjectId(applicationName),这样,公司用户可以通过公共成员资格提供程序对迁移的项目进行身份验证,但原始项目中的角色不会延续凭借不同的角色提供者。

所有标准会员管理策略,例如AspNet配置工具,Login控件,createuser向导,Membership功能(特别是Membership.DeleteUser() - 谢谢Thomas)将按预期运行,无需修改。

配置文件可以在任一方向上实现,使用成员资格提供程序的applicationId将允许配置文件数据跟随用户到任何关联项目。使用角色提供程序的不同ProjectId(applicationName)将允许每个项目中每个用户的单独配置文件。

更多细节和测试 here

下面列出了突出的配置部分,后面是修改后的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_regsql.exe配置Aspnet_db后,运行此脚本以修改aspnet_Users_DeleteUser sproc。

/*************************************************************/
/*************************************************************/
--- 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

答案 3 :(得分:0)

可能会在配置文件中存储值。在配置文件中设置配置文件条目,并使用它来存储值。

更现实地说,您可能希望将其存储在ASP.NET表之外,以便于使用和轻松访问值(如果需要,可能在Web环境之外)...

不确定您的所有要求是什么。