C#:检查用户是否可以访问服务器而不尝试连接

时间:2017-07-13 19:58:03

标签: c# sql-server

下午好,

我目前正面临一个问题,我有一个服务器名称列表,我需要验证一个经过Windows身份验证的用户是否具有指定服务器名称的权限,而不尝试建立与指定服务器的连接,他们的服务器列表。例如:

服务器:A,B,C,D

Joe拥有A和D的权限,但没有B和C的权限,所以Joe应该只在他的服务器列表中看到A和D.

我该如何解决这个问题?我应该从Active Directory中提取吗?我知道如何提取用户的身份,但是在哪里提取服务器信息并找出用户是否拥有权限是一个完全不同的故事。任何文档,代码示例,文章等都是有用的。

关于工作环境的说明

  1. 列表中的所有服务器都是运行SQL Server 2008 R2及更高版本的数据库服务器。
  2. 这是一个严格限制的政府环境。
  3. 我无法以任何方式修改Active Directory。
  4. 如果用户有权限,我可以整天查询Active Directory和SQL Server。
  5. 未授权使用第三方库和工具。
  6. 关于服务器列表的附注

    此列表存储在数据库中,但我不认为这对权限方面有帮助。我已经使用以下SQL查询解决了针对各个数据库检查权限的问题:

    SELECT name
    FROM sys.databases
    WHERE HAS_DBACCESS(name) = 1
    ORDER BY name
    

    感谢您的帮助!

    -Jamie

    结论

    基本上,我发现没有可能的方法来查询远程服务器上的AD组而不尝试建立连接,因此找到的所有样本的执行将使用IA标记用户帐户。对于其他有类似问题且不必担心IA会对用户施加压力的问题,我在下面列出了一些解决方案,您可以尝试满足您的需求(以下所有方法在正确实施时都能正常工作)。

    解决方案

    1. 查询活动目录中的服务器并从服务器检索组,如果失败9次,则异常消息将为“访问被拒绝”。如果成功,则继续拉出当前用户的组并与从服务器提取的组进行比较。此帖子的选定答案与this post和/或this post相结合,可以帮助您了解此解决方案所需的位置。
    2. 如果您已通过impersonation或其他方式(SQL Server Auth)访问服务器,则可以使用以下内容查看该成员是否分配了任何服务器角色:

      SELECT IS_SRVROLEMEMBER('public')

    3. 您还可以使用Microsoft.SqlServer.Management.Smo命名空间使用Microsoft.SqlServer.Smo程序集中的Login class。但是,您可能会或可能不会将Microsoft.SqlServer.SqlClrProvider命名空间从GAC移动到BIN。有关此内容的详细信息,请参阅this StackOverflow postthis Microsoft Connect主题,其中包含以下内容:

    4.   

      客户端应用程序不应使用Program Files文件夹中的程序集,除非它们来自特定的SDK文件夹(例如“C:\ Program Files(x86)\ Microsoft SQL Server \ 130 \ SDK”)

      1. 您甚至可以在try catch中执行基本连接测试,以查看连接是否有效。

        using (SqlConnection conn = new SqlConnection(connString)) { try { conn.Open(); conn.Close(); } catch (Exception e) { Console.Write($"Connection test failed: {e.Message}"); } }

      2. 实现总体目标的方法有很多种,这取决于您的具体情况以及您希望如何实现目标。对我来说,没有一个解决方案能够正常工作,因为在每个场景中,测试都会尝试连接到有问题的服务器,由于缺少权限,该服务器会标记用户。

2 个答案:

答案 0 :(得分:1)

如果您实施了Active Directory,那么您应该通过AD组向用户授予服务器等访问权限,否则会造成管理噩梦。想象一下,如果John Smith作为系统管理员加入您的公司,您是否会去每个服务器并明确指定他的权利?更容易创建服务器管理AD组然后将其分配给服务器(或者通过组策略指定服务器和权限级别上存在哪些AD组。

为什么这也有助于您在开发应用程序时,可以使用内置的AD角色提供程序来提供这样的功能。以下是按AD用户名

抓取用户组的简单示例
    using System.DirectoryServices.AccountManagement;

    public List<string> GetGroupNames(string userName)
    {
        List<string> result = new List<string>();
        using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "YOURDOMAINHERE"))
        {
            using (PrincipalSearchResult<Principal> src = UserPrincipal.FindByIdentity(pc, userName).GetGroups(pc))
            {
                src.ToList().ForEach(sr => result.Add(sr.SamAccountName));
            }
        }
        return result;
    }

编辑:所以如果你绝对拒绝使用活动目录组来管理服务器上的权限并且购买工具是不可能的,这里有一个类将遍历所有本地机器组并给你一个列表这些组中的用户。您可以执行某些操作,例如将其作为服务器上的计划任务(或获胜服务)运行,并将其结果保存回数据库,以便您可以随时查询或构建用于提取和监控此信息的UI。这并没有像您所说的那样伸出并获取sql server权限。

public class MachinePermissions
{
    string machineName { get; set; }
    public List<LocalGroup> localGroups { get; set; }

    public List<string> GetGroupMembers(string sGroupName)
    {
        List<String> myItems = new List<String>();
        GroupPrincipal oGroupPrincipal = GetGroup(sGroupName);
        PrincipalSearchResult<Principal> oPrincipalSearchResult = oGroupPrincipal.GetMembers();
        foreach (Principal oResult in oPrincipalSearchResult)
        {
            myItems.Add(oResult.Name);
        }
        return myItems;
    }

    private GroupPrincipal GetGroup(string sGroupName)
    {
        PrincipalContext oPrincipalContext = GetPrincipalContext();
        GroupPrincipal oGroupPrincipal = GroupPrincipal.FindByIdentity(oPrincipalContext, sGroupName);
        return oGroupPrincipal;
    }

    private PrincipalContext GetPrincipalContext()
    {
        PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Machine);
        return oPrincipalContext;
    }

    public MachinePermissions()
    {
        machineName = Environment.MachineName;
        PrincipalContext ctx = new PrincipalContext(ContextType.Machine, Environment.MachineName);
        GroupPrincipal gp = new GroupPrincipal(ctx);
        gp.Name = "*";
        PrincipalSearcher ps = new PrincipalSearcher();
        ps.QueryFilter = gp;
        PrincipalSearchResult<Principal> result = ps.FindAll();
        if(result.Count() > 0)
        {
            localGroups = new List<LocalGroup>();
            foreach (Principal p in result)
            {
                LocalGroup g = new LocalGroup();
                g.groupName = p.Name;
                g.users = GetGroupMembers(g.groupName);
                localGroups.Add(g);
            }
        }
    }
}

public class LocalGroup
{
    public string groupName { get; set; }
    public List<String> users { get; set; }
}

答案 1 :(得分:0)

您可以创建AD组以访问每个数据库,然后将用户添加到它们。 在您的应用程序中,您可以添加组列表并检查用户是否是其成员。

这是常见做法,允许为不同用户创建不同访问权限的安全方案。您只需为组设置一次权限,所有成员都可以从访问权限中受益。