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

7 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. 未经授权使用第三方库和工具.

关于服务器列表的附注

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

SELECT name
FROM sys.databases
WHERE HAS_DBACCESS(name) = 1
ORDER BY name
Run Code Online (Sandbox Code Playgroud)

谢谢您的帮助!

-杰米

结论

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

解决方案

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

    SELECT IS_SRVROLEMEMBER('public')

  3. 您还可以使用Microsoft.SqlServer.Management.Smo命名空间使用Microsoft.SqlServer.Smo程序集中提供的Login类.但是,您可能会或可能不会将Microsoft.SqlServer.SqlClrProvider命名空间从GAC移动到BIN.有关此内容的更多信息,请参阅此StackOverflow帖子,以及 Microsoft Connect线程,其中说明了以下内容:

客户端应用程序不应使用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}"); } }

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

Tra*_*ton 2

如果您实施了 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;
    }
Run Code Online (Sandbox Code Playgroud)

编辑:因此,如果您绝对拒绝使用活动目录组来管理服务器上的权限,并且购买工具是不可能的,那么这里有一个类将迭代所有本地计算机组并为您提供这些组中的用户列表组。您可以执行一些操作,例如让它作为计划任务在服务器(或赢得服务)上运行,并将其结果保存回数据库,以便您可以查询或构建 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; }
}
Run Code Online (Sandbox Code Playgroud)