• .NET C#执行程序功能时根据Windows用户角色动态提权执行相关业务功能的方法


    有时候,我们的应用程序功能和业务组成比较复杂,需要使用的组件、子程序众多,可能牵涉到桌面客户端程序,bs网页端程序,甚至还有桌面内嵌web的应用程序组成形式。

    我们在执行应用程序过程中,登录的Windows操作系统的用户角色也不尽相同,用户可能多数是使用的管理员组的普通用户账户登录,开发人员有时候需要提权,使用的是Administrator账户。

    应用程序在执行时如果有内嵌web功能,比如网页版的剪贴板,截图工具等功能,可能启动该应用程序时,可能只需要使用当前用户所属的”普通“权限即可(超管的普通权限就是超管权限;普通用户的普通权限就是普通权限,与此相对的是普通用户的右键”以管理员身份运行“)。

    但是到了某些具体的业务环节时,比如操作注册COM组件之类的,可能有时需要使用管理员权限。如果此时是使用的普通账户登录的,这个时候就需要在代码功能实现里动态对此功能执行时进行提权,否则会操作不成功。

    一般我们代码操作注册某些组件可以直接使用Process方式。需要提权时在Process的参数类ProcessStartInfo的Verb属性指定为”runas“即可。

    当然碰到上面描述的场景,我们第一想法就是指定”runas“,但这样打包出来的程序会带有小盾牌,打开应用程序exe时会弹框要求提权,这样又影响了其他普通权限的功能,可能又得考虑降权,改动量较大。这不是一个好的方式。

    有人说需要配置app.manifest里requestedPrivileges 节点的requestedExecutionLevel节 属性level值为requireAdministrator,试验了下,同样会带有小盾牌,执行应用程序exe就让提权,此方案走不通。

    后来找到了Windows底层的一个函数,ShellExecuteEx,解决了此问题。

    中间还走了一个弯路,不区分当前操作系统登录用户角色身份,都按照ShellExecuteEx方式进行处理。测试人员在使用时,当操作系统登录用户为Administrator时,发现ShellExecuteEx方法执行报错,其实可想而知,超级管理员已经是最高权限了,再提权实际没有意义。

    综合Process、ShellExecuteEx,以及Administrator和普通用户的使用场景,找到了一个折中处理方案,可能不是最好的,如有更好思路,欢迎各路大神提供参考意见。现把整体解决思路整理如下。

    0.打开应用程序exe时不设置任何提权操作代码功能。动态判断当前Windows用户的角色,如果是Administrator,那么他的权限已经足够使用,在执行COM时直接使用Process方式,它的参数类ProcessStartInfo的Verb默认为空即可。

    如果是普通用户(带有管理员组权限),那么他在执行其他业务时,使用普通权限即可(这样不影响一些内嵌网页组件程序工作),当执行COM时调用ShellExecuteEx进行动态提权,以保证COM组件执行成功。如果执行失败,可以参考我之前的帖子来获取具体执行失败的原因,帖子地址:https://www.cnblogs.com/jeff151013/p/16080757.html。

    1.辅助方法,获取当前Windows登录用户角色身份

    using System.Security.Principal;

    public static class UserRoleCheckUtils
        {
            public static bool? OsCurrentUserIsAdministrator(out Exception ex)
            {
                bool? isAdministrator = null;
                ex = null;
                try
                {
                    var windowsIdentity = WindowsIdentity.GetCurrent();
                    var windowsPrincipal = new WindowsPrincipal(windowsIdentity);

                    isAdministrator = windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
                }
                catch (Exception exception)
                {
                    ex = exception;
                }
                return isAdministrator;
            }
        }

    2.工具类1 ShellUtils,上代码

    using System.Runtime.InteropServices;

    public class ShellUtils
        {
            #region ShellExecute
            public enum ShowCommands : int
            {
                SW_HIDE = 0,
                SW_SHOWNORMAL = 1,
                SW_NORMAL = 1,
                SW_SHOWMINIMIZED = 2,
                SW_SHOWMAXIMIZED = 3,
                SW_MAXIMIZE = 3,
                SW_SHOWNOACTIVATE = 4,
                SW_SHOW = 5,
                SW_MINIMIZE = 6,
                SW_SHOWMINNOACTIVE = 7,
                SW_SHOWNA = 8,
                SW_RESTORE = 9,
                SW_SHOWDEFAULT = 10,
                SW_FORCEMINIMIZE = 11,
                SW_MAX = 11
            }
            [DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            static extern IntPtr ShellExecute(
                IntPtr hwnd,
                string lpOperation,
                string lpFile,
                string lpParameters,
                string lpDirectory,
                ShowCommands nShowCmd);

            #endregion

            #region ShellExecuteEx
            private const int SwShow = 5;
            private const uint SeeMaskInvokeidlist = 12;

            /// <summary>
            /// 对指定应用程序执行某个操作
            /// </summary>
            /// <param name="lpExecInfo"></param>
            /// <returns></returns>
            [DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            public static extern bool ShellExecuteEx(ref Shellexecuteinfo lpExecInfo);
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
            public struct Shellexecuteinfo
            {
                /// <summary>
                /// 结构大小,以字节为单位
                /// </summary>
                public int cbSize;
                /// <summary>
                /// 一个标志数组,用来设置其他成员的有效性。
                /// </summary>
                public uint fMask;
                /// <summary>
                /// 可选。执行ShellExecuteEx的窗口句柄,可设为NULL。
                /// </summary>
                public IntPtr hwnd;
                /// <summary>
                /// 指定执行的动作,包括:edit ,explore ,find ,open,print, properties
                /// </summary>
                [MarshalAs(UnmanagedType.LPTStr)]
                public string lpVerb;
                /// <summary>
                /// 以\0 结尾的字符串,指出 lpVerb 的操作对象的路径,被系统支持的操作包括文本的 open 、 print等
                /// </summary>
                [MarshalAs(UnmanagedType.LPTStr)]
                public string lpFile;
                /// <summary>
                /// 可选。运行/打开程序的参数,如果打开的是一个文档,则该项无效
                /// </summary>
                [MarshalAs(UnmanagedType.LPTStr)]
                public string lpParameters;
                /// <summary>
                /// 可选。指明工作目录的名字,成员没有说明,则默认为当前目录
                /// </summary>
                [MarshalAs(UnmanagedType.LPTStr)]
                public string lpDirectory;
                /// <summary>
                /// 必须。指定打开的程序的显示方式,为SW_值中的一个。
                /// </summary>
                public int nShow;
                /// <summary>
                /// 【out】如果设置SEE_MASK_NOCLOSEPROCESS S值并且ShellExecuteEx 调用成功,则该项的值大于32,如果调用失败,则将设置为 SE_ERR_XXX 的错误值。
                /// </summary>
                public IntPtr hInstApp;
                /// <summary>
                /// 一个ITEMIDLIST结构的地址,用来存储成员的特别标识符,当fMask不包括SEE_MASK_IDLIST或SEE_MASK_INVOKEIDLIST时该项被忽略
                /// </summary>
                public IntPtr lpIDList;
                /// <summary>
                /// 用以指明文件类别的名字或GUID,当fMask不包括SEE_MASK_CLASSNAME时该项被忽略
                /// </summary>
                [MarshalAs(UnmanagedType.LPTStr)]
                public string lpClass;
                /// <summary>
                /// 获得已在系统注册的文件类型的Handle,当fMask不包括SEE_MASK_HOTKEY时该项被忽略
                /// </summary>
                public IntPtr hkeyClass;
                /// <summary>
                /// 程序的热键关联,低位存储虚拟关键码(Key Code),高位存储修改标志位(HOTKEYF_),修改标志为(modifier flags)的详细列表请看WM_SETHOTKEY消息的描述,当fmask不包括SEE_MASK_HOTKEY时该项被忽略
                /// </summary>
                public uint dwHotKey;
                /// <summary>
                /// 取得对应文件类型的图标的Handle,当fMask不包括SEE_MASK_ICON时该项被忽略
                /// </summary>
                public IntPtr hIcon;
                /// <summary>
                /// 指向新启动的程序的句柄。若fMask不设为SEE_MASK_NOCLOSEPROCESS则该项值为NULL。但若程序没有启动,即使fMask设为SEE_MASK_NOCLOSEPROCESS,该值也仍为NULL。
                /// </summary>
                public IntPtr hProcess;
            }

            public static bool Execute(string verb, string fileFullPath, string arguments)
            {
                ShellUtils.Shellexecuteinfo info = new ShellUtils.Shellexecuteinfo();
                info.cbSize = Marshal.SizeOf(info);
                info.fMask = SeeMaskInvokeidlist;
                info.lpVerb = verb;
                info.lpFile = fileFullPath;
                info.lpParameters = arguments;
                info.nShow = SwShow;

                return ShellExecuteEx(ref info);
            }

            #endregion
        }

    3.工具类2 AddinHelper,主要是提供操作系统登录用户角色判断、Process方法调用和ShellUtils.Execute方法的调用

    {
        public static bool RegisterAddIn(string dllPath, bool isBit64)
        {
            string winDir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
            string regasmPath;
            if (isBit64)
            {
                regasmPath = Path.Combine(winDir, @"Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe");
            }
            else
            {
                regasmPath = Path.Combine(winDir, @"Microsoft.NET\Framework\v4.0.30319\RegAsm.exe");
            }
       //获取当前操作系统登录用户角色是否是超级管理员
            var osCurrentUserIsAdministrator = UserRoleCheckUtils.OsCurrentUserIsAdministrator(out Exception ex);
            //如果是超管、使用process方式注册;否则使用ShellExecuteEx方式
            if (osCurrentUserIsAdministrator.HasValue && osCurrentUserIsAdministrator.Value)
            {
                return UseProcessOperateAddIn(regasmPath, " /codebase \"" + dllPath + "\"");
            }
            //如果获取用户身份失败或者是普通用户,将提权注册插件
            return ShellUtils.Execute("runas", regasmPath, dllPath);
        }

        /// <summary>
        /// 操作COM插件
        /// </summary>
        /// <param name="regasmPath"></param>
        /// <param name="dllPath">含其他操作命令</param>
        /// <returns></returns>
        public static bool UseProcessOperateAddIn(string regasmPath, string dllPath)
        {
            try
            {
                var startInfo = new ProcessStartInfo();
                startInfo.Verb = string.Empty;
                startInfo.FileName = regasmPath;
                startInfo.Arguments = dllPath;
                startInfo.WindowStyle = ProcessWindowStyle.Normal;
                startInfo.RedirectStandardOutput = true;
                startInfo.RedirectStandardError = true;
                startInfo.RedirectStandardInput = true;
                startInfo.UseShellExecute = false;
                startInfo.CreateNoWindow = true; //让窗体不显示
                startInfo.ErrorDialog = false;
                Process process = Process.Start(startInfo);
                process.EnableRaisingEvents = true;
                process.WaitForExit();
                StreamReader reader = process.StandardOutput; //截取输出流
                string output = process.StandardError.ReadToEnd();
                var rtn = process.ExitCode;
                if (rtn != 0)
                    throw new Exception(output);
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
    }

  • 相关阅读:
    多线程-死锁代码示例
    区块链技术:以太方学习文档
    svn 不能校验路径“XXX”的锁;没有匹配的可用锁令牌 故障解决方法
    Oracle ORA-27102的解决办法(out of memory)
    Linux常用命令语法+示例
    Java如何实现form表单提交的数据自动对应实体类(源码)
    Java分布式锁看这篇就够了
    quartz时间配置
    volatile 实现原理
    == 和 equals()的区别
  • 原文地址:https://www.cnblogs.com/jeff151013/p/16081619.html
Copyright © 2020-2023  润新知