这里来记录下今天在项目中遇到的一个小问题:
我们自己打包的的一个windows服务想要访问远程共享盘下面的文件,本以为是一个很简单的功能,不就是远程访问吗,可事实并非如此,其中测试的时候,程序以exe可执行文件运行,OK这时候没毛病,一切正常,当我们打包服务的时候问题就出现了,错误日志显示:Access to the path \'\\xxxxxx\map\2020\0705\20B13748-48.txt\' is denied. 权限不够,借助度娘查阅了一些资料,大多说的都是修改目标服务器的权限,可是这个是客户重要服务器,我们是不能动的,这种方案就被pass掉了,我们经理提出了一种方案:
给这个服务创建访问账号和密码,不要使用系统本身的账号的登录
设置过之后返现访问成功了,可是当我们重新卸载,安装服务的时候返现又不行了,又得重新设置过一遍,这种方式也不适合我们,当然你不需要重新安装服务这种方案是可行的。
最后我们又找到一种方法,用代码的方式在运行的时候使用凭证登陆一次共享盘,其中的原理我也不是很清楚,在这里无法向大家做具体解释
namespace Read { class Program { static Program() { NativeCredMan.WriteCred("共享盘ip", @"共享盘账号", "共享盘密码",CRED_TYPE.DOMAIN_PASSWORD, CRED_PERSIST.ENTERPRISE); } static void Main(string[] args) { while (true) { try { var filePath = @"\\xxxxx\map\2020\0705\20B13748-48.txt"; StreamReader sr = new StreamReader(filePath, Encoding.Default); StringBuilder sb = new StringBuilder(""); try { String line; int ind = 0; while ((line = sr.ReadLine()) != null) { ind++; if (!string.IsNullOrEmpty(line) && line.StartsWith(".")) { sb.Append(line.ToString()); } } Log.Write($"读取成功=>{sb.ToString()}"); } catch (Exception e) { Log.WriteLog(e); } } catch (Exception e) { Log.WriteLog(e); Console.WriteLine($"读取失败=>{e.Message}"); } Thread.Sleep(5000); } } } } public class CertificateManager { /// <summary> /// 凭据类型 /// </summary> public enum CRED_TYPE : uint { //普通凭据 GENERIC = 1, //域密码 DOMAIN_PASSWORD = 2, //域证书 DOMAIN_CERTIFICATE = 3, //域可见密码 DOMAIN_VISIBLE_PASSWORD = 4, //一般证书 GENERIC_CERTIFICATE = 5, //域扩展 DOMAIN_EXTENDED = 6, //最大 MAXIMUM = 7, // Maximum supported cred type MAXIMUM_EX = (MAXIMUM + 1000), // Allow new applications to run on old OSes } //永久性 public enum CRED_PERSIST : uint { SESSION = 1, //本地计算机 LOCAL_MACHINE = 2, //企业 ENTERPRISE = 3, } internal class NativeCredMan { [DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)] //读取凭据信息 static extern bool CredRead(string target, CRED_TYPE type, int reservedFlag, out IntPtr CredentialPtr); [DllImport("Advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)] //增加凭据 static extern bool CredWrite([In] ref NativeCredential userCredential, [In] UInt32 flags); [DllImport("Advapi32.dll", EntryPoint = "CredFree", SetLastError = true)] static extern bool CredFree([In] IntPtr cred); [DllImport("Advapi32.dll", EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode)] //删除凭据 static extern bool CredDelete(string target, CRED_TYPE type, int flags); //[DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)] //static extern bool CredEnumerateold(string filter, int flag, out int count, out IntPtr pCredentials); [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern bool CredEnumerate(string filter, uint flag, out uint count, out IntPtr pCredentials); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct NativeCredential { public UInt32 Flags; public CRED_TYPE Type; public IntPtr TargetName; public IntPtr Comment; public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten; public UInt32 CredentialBlobSize; public IntPtr CredentialBlob; public UInt32 Persist; public UInt32 AttributeCount; public IntPtr Attributes; public IntPtr TargetAlias; public IntPtr UserName; internal static NativeCredential GetNativeCredential(Credential cred) { var ncred = new NativeCredential { AttributeCount = 0, Attributes = IntPtr.Zero, Comment = IntPtr.Zero, TargetAlias = IntPtr.Zero, //Type = CRED_TYPE.DOMAIN_PASSWORD, Type = cred.Type, Persist = (UInt32)cred.Persist, CredentialBlobSize = (UInt32)cred.CredentialBlobSize, TargetName = Marshal.StringToCoTaskMemUni(cred.TargetName), CredentialBlob = Marshal.StringToCoTaskMemUni(cred.CredentialBlob), UserName = Marshal.StringToCoTaskMemUni(cred.UserName) }; return ncred; } } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct Credential { public UInt32 Flags; public CRED_TYPE Type; public string TargetName; public string Comment; public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten; public UInt32 CredentialBlobSize; public string CredentialBlob; public CRED_PERSIST Persist; public UInt32 AttributeCount; public IntPtr Attributes; public string TargetAlias; public string UserName; } /// <summary> /// 向添加计算机的凭据管理其中添加凭据 /// </summary> /// <param name="key">internet地址或者网络地址</param> /// <param name="userName">用户名</param> /// <param name="secret">密码</param> /// <param name="type">密码类型</param> /// <param name="credPersist"></param> /// <returns></returns> public static int WriteCred(string key, string userName, string secret, CRED_TYPE type, CRED_PERSIST credPersist) { var byteArray = Encoding.Unicode.GetBytes(secret); if (byteArray.Length > 512) throw new ArgumentOutOfRangeException("The secret message has exceeded 512 bytes."); var cred = new Credential { TargetName = key, CredentialBlob = secret, CredentialBlobSize = (UInt32)Encoding.Unicode.GetBytes(secret).Length, AttributeCount = 0, Attributes = IntPtr.Zero, UserName = userName, Comment = null, TargetAlias = null, Type = type, Persist = credPersist }; var ncred = NativeCredential.GetNativeCredential(cred); var written = CredWrite(ref ncred, 0); var lastError = Marshal.GetLastWin32Error(); if (written) { return 0; } var message = ""; if (lastError == 1312) { message = (string.Format("Failed to save " + key + " with error code {0}.", lastError) + " This error typically occurrs on home editions of Windows XP and Vista. Verify the version of Windows is Pro/Business or higher."); } else { message = string.Format("Failed to save " + key + " with error code {0}.", lastError); } return 1; } /// <summary> /// 读取凭据 /// </summary> /// <param name="targetName"></param> /// <param name="credType"></param> /// <param name="reservedFlag"></param> /// <param name="intPtr"></param> /// <returns></returns> public static bool WReadCred(string targetName, CRED_TYPE credType, int reservedFlag, out IntPtr intPtr) { return CredRead(targetName, CRED_TYPE.DOMAIN_PASSWORD, reservedFlag, out intPtr); } /// <summary> /// 删除凭据 /// </summary> /// <param name="target"></param> /// <param name="type"></param> /// <param name="flags"></param> /// <returns></returns> public static bool DeleteCred(string target, CRED_TYPE type, int flags) { return CredDelete(target, type, flags); } } }
希望这个几个方式能对大家有所帮助。