65.9K
CodeProject 正在变化。 阅读更多。
Home

WinForms - 使用自定义主体与 AspNetDb

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.40/5 (5投票s)

2010年6月1日

CPOL

3分钟阅读

viewsIcon

53463

downloadIcon

1868

描述如何在 WinForms 应用程序中使用带有 AspNetDb 安全数据库的自定义 principal 实现。

引言

我们的许多 ASP.NET 应用程序使用带有 SqlRoleProvider 的 AspNetDb 进行安全和组管理。 .NET 附带的 Membership API 在避免这种标准身份验证行为的代码重复方面非常出色。

您的 WinDorms 应用程序可以使用与 ASP.NET 应用程序相同的 API,但它必须引用 System.Web 库。

但是,对于许多应用程序来说,并不需要这个完整的 API。 我们只需要找出用户是哪些组的成员,并根据成员资格授予他们对资源的访问权限。 本文将演示一种使用 AspNetDb 进行用户角色管理但绕过 Membership API 的技术。

背景场景

您可能会遇到这样一种情况:您想使用标准的 Windows 身份名称,但不希望使用 Active Directory 进行组管理。 您希望自定义角色成员资格的控制权。 这可能是因为您想将所有应用程序安全保存在一个地方(即 AspNetDb),或者您只是想将应用程序特定的安全保存在域之外。 域管理员可能不想负责创建和分配应用程序的角色成员资格。

特别是对于内部公司应用程序,用户希望采用“单点登录”方法来访问应用程序。 他们使用他们的 Windows 凭据登录到他们的终端,这应该允许他们访问他们需要的所有应用程序;为不同的应用程序单独登录会让人感到厌倦。 因此,我们已经有了身份验证过程的“用户名”部分。 这是他们在 Windows 凭据中指定的名称。 现在,我们只需要提供测试组成员资格的功能。

数据库

如果您还没有 AspNetDb 的实例,以下文章定义了如何创建数据库。

注意:您需要选择一个 SQL Server 实例,而不是您的本地计算机。

数据库管理

我们使用 IDesign Credentials Manager,它比基于 IDE 的“Web Application Tool”更可取,因为它允许任何人管理凭据,而不仅仅是那些安装了 VS2005 IDE 的人。

我们使用此应用程序的自定义版本,它允许我们管理远程 SQL 数据库(源代码将在获得许可后提供)。

创建并分配用户

Credentials Manager 提供了一个用于管理 ASP.NET 安全模型的界面。 创建您的应用程序名称,将用户和角色分配给应用程序,然后将用户添加到角色中。

AppName.png

图 1 - 创建您的应用程序名称

UserName.png

图 2 - 在应用程序中创建用户

UserRoleAssign.png

图 3 - 将用户分配给角色

Using the Code

您的应用程序现在应该检查应用程序以查看用户是哪些角色的成员。 我们可以使用 IPrincipalIIdentity 的自定义实现来做到这一点。 这些实现基于构成 CSLA.Net framework 的代码,但它们已进行自定义以使用从 AspNetDb 提供角色管理的过程。

IIdentity 的实现

namespace System.Security
{
    using System;
    using System.Collections;
    using System.Configuration;
    using System.Security.Principal;
    using System.Data;
    using System.Data.SqlClient;

    /// <summary>
    /// Implements a custom Identity class for use with the AspNetDb
    /// </summary>
    [Serializable()]
    public class AspNetDbIdentity : IIdentity
    {
        string usernameField = string.Empty;
        ArrayList rolesList = new ArrayList();

        #region IIdentity

        /// <summary>
        /// Implements the IsAuthenticated property
        /// defined by IIdentity. Returns True
        /// if the user is a member of at least 1 role
        /// </summary>
        bool IIdentity.IsAuthenticated
        {
            get
            {
                return (rolesList.Count > 0);
            }
        }

        /// <summary>
        /// Implements the AuthenticationType property defined by IIdentity.
        /// </summary>
        string IIdentity.AuthenticationType
        {
            get
            {
                return "AspNetDb";
            }
        }

        /// <summary>
        /// Implements the Name property defined by IIdentity.
        /// </summary>
        string IIdentity.Name
        {
            get
            {
                return usernameField;
            }
        }

        #endregion

        internal bool IsInRole(string role)
        {
            return rolesList.Contains(role);
        }

        #region Ctor

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="userName">The name of the user defined in the AspNetDb
        public AspNetDbIdentity(string userName) 
        {
            this.usernameField = userName;
            this.rolesList.Clear();

            using (SqlConnection cn = new SqlConnection(
                   ConfigurationManager.ConnectionStrings[
                   "SecurityConnectionString"].ConnectionString))
            {
                cn.Open();
                using (SqlCommand cm = cn.CreateCommand())
                {
                    cm.CommandText = "aspnet_UsersInRoles_GetRolesForUser";
                    cm.CommandType = CommandType.StoredProcedure;
                    cm.Parameters.AddWithValue("@ApplicationName", 
                      ConfigurationManager.AppSettings["ApplicationName"]);
                    cm.Parameters.AddWithValue("@UserName", userName);
                    using (SqlDataReader dr = cm.ExecuteReader())
                    {
                        while (dr.Read())
                        {
                            rolesList.Add(dr.GetString(0));
                        }
                    }
                }
            }
        }

        #endregion

    }
}

正如您将在构造中看到的那样,您需要在应用程序的App.config文件中定义两个应用程序设置才能使应用程序正常工作。 确保您先设置这两个设置。

  • 为您的 SQL 实例定义一个连接字符串,该实例托管 AspNetDb
  • <connectionstrings>
        <add name="SecurityConnectionString" 
           connectionstring="Data Source=YOURSERVER;
             Initial Catalog=AspNetDb;User ID=MyUser;Password=Password" 
           providername="System.Data.SqlClient">
    </add>  
      
    </connectionstrings>
  • 为您设置的应用程序定义一个设置。 这应该与您在 AspNetDb 中设置的应用程序名称匹配。
  • <appsettings>
        <add key="ApplicationName" value="Harmony">
      </add>
    </appsettings>

IPrincipal 的实现

namespace System.Security
{
    using System;
    using System.Security.Principal;
    using System.Threading;

    /// <summary>
    /// Implements a custom Principal class that used the AspNetDb database
    /// to check for role membership
    /// </summary>
    [Serializable()]
    public class AspNetDbPrincipal : IPrincipal
    {
        AspNetDbIdentity identityField;

        #region IPrincipal

        /// <summary>
        /// Implements the Identity property defined by IPrincipal.
        /// </summary>
        IIdentity IPrincipal.Identity
        {
            get
            {
                return identityField;
            }
        }

        /// <summary>
        /// Implements the IsInRole property defined by IPrincipal.
        /// </summary>
        bool IPrincipal.IsInRole(string role)
        {
            return identityField.IsInRole(role);
        }

        #endregion

        #region Ctor

        /// <summary>
        /// Default construct
        /// </summary>
        /// <param name="identity">The AspNetDbIdentity assigned to the user
        public AspNetDbPrincipal(AspNetDbIdentity identity)
        {
            AppDomain currentdomain = Thread.GetDomain();
            currentdomain.SetPrincipalPolicy(PrincipalPolicy.UnauthenticatedPrincipal);

            IPrincipal oldPrincipal = Thread.CurrentPrincipal;
            Thread.CurrentPrincipal = this;

            try
            {
                if (!(oldPrincipal.GetType() == typeof(AspNetDbPrincipal)))
                    currentdomain.SetThreadPrincipal(this);
            }
            catch
            {
                // failed, but we don't care because there's nothing
                // we can do in this case
            }
            identityField = identity;
        }

        #endregion
    }
}

使用对象

构造对象遵循与 WindowsPrincipalWindowsIdentity 对象相同的模式。 将 Environment.Username 传递给 AspNetDbIdentity,然后使用 AspNetDbIdentity 实例来创建 AspNetDbPrincipal

AspNetDbIdentity identity = new AspNetDbIdentity(Environment.UserName)
AspNetDbPrincipal principal = new AspNetDbPrincipal(identity);

应用程序安全检查

构造 AspNetDbPrincipal 对象会将主线程设置为 AspNetDbPrincipal 实例,因此您可以通过访问 Thread.CurrentPrincipal 对象来使用编程安全或声明式安全。

// This checks that the AspNetDbPrincipal is a member of the administrator role
Thread.CurrentPrincipal.IsInRole("Administrator");

测试应用程序

本文附带的测试应用程序演示了使用来自 AspNetDb 的数据在 WinForms 应用程序中实现基于角色的安全所需的所有设置。

TestAppScreenShot.png

历史

  • 2010 年 6 月 - 初始版本。
© . All rights reserved.