C# 如何在连接到网络共享时提供用户名和密码
当连接到当前用户(在我的情况下是启用网络的服务用户)没有权限的网络共享时,必须提供名称和密码 我知道如何使用Win32函数(来自C# 如何在连接到网络共享时提供用户名和密码,c#,.net,winapi,networking,passwords,C#,.net,Winapi,Networking,Passwords,当连接到当前用户(在我的情况下是启用网络的服务用户)没有权限的网络共享时,必须提供名称和密码 我知道如何使用Win32函数(来自mpr.dll的WNet*系列)实现这一点,但我希望使用.Net(2.0)功能实现这一点 有哪些选择 也许更多信息会有所帮助: var impersonationContext = new WrappedImpersonationContext(Domain, Username, Password); impersonationContext.Enter(); //d
mpr.dll
的WNet*
系列)实现这一点,但我希望使用.Net(2.0)功能实现这一点
有哪些选择
也许更多信息会有所帮助:
var impersonationContext = new WrappedImpersonationContext(Domain, Username, Password);
impersonationContext.Enter();
//do your stuff here
impersonationContext.Leave();
- 用例是windows服务,而不是Asp.Net应用程序
- 该服务正在一个没有共享权限的帐户下运行
- 客户端不知道共享所需的用户帐户
- 客户端和服务器不是同一域的成员
<identity impersonate="true" userName="domain\user" password="****" />
进入您的web.config
一个可能有效的选项是使用
WindowsIdentity.Impersonate
(并更改线程主体)成为所需的用户。回到p/invoke,不过,我恐怕
另一个厚颜无耻(同样远离理想)的选择可能是产生一个流程来完成工作ProcessStartInfo
接受.UserName
、.Password
和.Domain
最后-可能在有访问权限的专用帐户中运行服务?(已删除,因为您已澄清这不是一个选项)。如果您无法创建本地有效的安全令牌,那么您似乎已经排除了Win32 API和WNetAddConnection*的所有选项 MSDN上关于WNet的大量信息-PInvoke信息和连接到UNC路径的示例代码如下: 此处的MSDN参考:
您可以更改线程标识,也可以p/Invoke WNetAddConnection2。我更喜欢后者,因为有时我需要为不同的位置维护多个凭据。我将其包装到一个IDisposable中,然后调用WNetCancelConnection2删除cred(避免出现多个用户名错误): 我非常喜欢的答案,我做了自己的快速实现。如果其他人急着需要它,它就在这里:
public class NetworkConnection : IDisposable
{
string _networkName;
public NetworkConnection(string networkName,
NetworkCredential credentials)
{
_networkName = networkName;
var netResource = new NetResource()
{
Scope = ResourceScope.GlobalNetwork,
ResourceType = ResourceType.Disk,
DisplayType = ResourceDisplaytype.Share,
RemoteName = networkName
};
var userName = string.IsNullOrEmpty(credentials.Domain)
? credentials.UserName
: string.Format(@"{0}\{1}", credentials.Domain, credentials.UserName);
var result = WNetAddConnection2(
netResource,
credentials.Password,
userName,
0);
if (result != 0)
{
throw new Win32Exception(result);
}
}
~NetworkConnection()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
WNetCancelConnection2(_networkName, 0, true);
}
[DllImport("mpr.dll")]
private static extern int WNetAddConnection2(NetResource netResource,
string password, string username, int flags);
[DllImport("mpr.dll")]
private static extern int WNetCancelConnection2(string name, int flags,
bool force);
}
[StructLayout(LayoutKind.Sequential)]
public class NetResource
{
public ResourceScope Scope;
public ResourceType ResourceType;
public ResourceDisplaytype DisplayType;
public int Usage;
public string LocalName;
public string RemoteName;
public string Comment;
public string Provider;
}
public enum ResourceScope : int
{
Connected = 1,
GlobalNetwork,
Remembered,
Recent,
Context
};
public enum ResourceType : int
{
Any = 0,
Disk = 1,
Print = 2,
Reserved = 8,
}
public enum ResourceDisplaytype : int
{
Generic = 0x0,
Domain = 0x01,
Server = 0x02,
Share = 0x03,
File = 0x04,
Group = 0x05,
Network = 0x06,
Root = 0x07,
Shareadmin = 0x08,
Directory = 0x09,
Tree = 0x0a,
Ndscontainer = 0x0b
}
好的。。。我可以重新考虑
免责声明:我刚刚度过了一天18个多小时(再次)。。我老了,健忘了。。我不会拼写。。我的注意力持续时间很短,所以我最好能快速做出反应
问题:
是否可以将线程主体更改为本地计算机上没有帐户的用户 答复: 是的,您可以更改线程主体,即使您使用的凭据未在本地定义或位于“林”之外 我只是在尝试从服务通过NTLM身份验证连接到SQL server时遇到了这个问题。此调用使用与进程关联的凭据,这意味着您需要本地帐户或域帐户进行身份验证,然后才能模拟。废话,废话 但是 调用属性为???\u NEW\u CREDENTIALS的LogonUser(..)将返回一个安全令牌,而不尝试验证凭据。基尔。。不必在“林”中定义帐户。拥有令牌后,可能需要调用DuplicateToken(),并选择启用模拟以生成新令牌。现在调用SetThreadToken(NULL,token);(可能是令牌(&T))。。对ImpersonalLoggedOnUser(令牌)的调用;可能需要,但我不这么认为。查一查 做你需要做的 如果调用ImpersonalLoggeDonUser()则调用RevertToSelf(),然后调用SetThreadToken(NULL,NULL);(我想…查找),然后在创建的句柄上关闭句柄()
没有承诺,但这对我有用。。。这是我的头顶(就像我的头发一样),我不会拼写 我搜索了很多方法,我用自己的方式做了。您必须通过命令提示符NET USE command打开两台机器之间的连接,并在完成工作后使用命令提示符NET USE“myconnection”/delete清除连接 必须从代码隐藏中使用命令提示过程,如下所示:
var savePath = @"\\servername\foldername\myfilename.jpg";
var filePath = @"C:\\temp\myfileTosave.jpg";
用法很简单:
SaveACopyfileToServer(filePath, savePath);
以下是功能:
using System.IO
using System.Diagnostics;
public static void SaveACopyfileToServer(string filePath, string savePath)
{
var directory = Path.GetDirectoryName(savePath).Trim();
var username = "loginusername";
var password = "loginpassword";
var filenameToSave = Path.GetFileName(savePath);
if (!directory.EndsWith("\\"))
filenameToSave = "\\" + filenameToSave;
var command = "NET USE " + directory + " /delete";
ExecuteCommand(command, 5000);
command = "NET USE " + directory + " /user:" + username + " " + password;
ExecuteCommand(command, 5000);
command = " copy \"" + filePath + "\" \"" + directory + filenameToSave + "\"";
ExecuteCommand(command, 5000);
command = "NET USE " + directory + " /delete";
ExecuteCommand(command, 5000);
}
ExecuteCommand函数也是:
public static int ExecuteCommand(string command, int timeout)
{
var processInfo = new ProcessStartInfo("cmd.exe", "/C " + command)
{
CreateNoWindow = true,
UseShellExecute = false,
WorkingDirectory = "C:\\",
};
var process = Process.Start(processInfo);
process.WaitForExit(timeout);
var exitCode = process.ExitCode;
process.Close();
return exitCode;
}
这个函数对我来说运行得非常快和稳定 Luke Quinane解决方案看起来不错,但在我的ASP.NET MVC应用程序中只起了部分作用。在同一台服务器上有两个具有不同凭据的共享,我只能对第一个使用模拟 WNetAddConnection2的问题还在于它在不同的windows版本上的行为不同。这就是我寻找替代方案并找到函数的原因。以下是我的代码,它也适用于ASP.NET:
public sealed class WrappedImpersonationContext
{
public enum LogonType : int
{
Interactive = 2,
Network = 3,
Batch = 4,
Service = 5,
Unlock = 7,
NetworkClearText = 8,
NewCredentials = 9
}
public enum LogonProvider : int
{
Default = 0, // LOGON32_PROVIDER_DEFAULT
WinNT35 = 1,
WinNT40 = 2, // Use the NTLM logon provider.
WinNT50 = 3 // Use the negotiate logon provider.
}
[DllImport("advapi32.dll", EntryPoint = "LogonUserW", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain,
String lpszPassword, LogonType dwLogonType, LogonProvider dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll")]
public extern static bool CloseHandle(IntPtr handle);
private string _domain, _password, _username;
private IntPtr _token;
private WindowsImpersonationContext _context;
private bool IsInContext
{
get { return _context != null; }
}
public WrappedImpersonationContext(string domain, string username, string password)
{
_domain = String.IsNullOrEmpty(domain) ? "." : domain;
_username = username;
_password = password;
}
// Changes the Windows identity of this thread. Make sure to always call Leave() at the end.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Enter()
{
if (IsInContext)
return;
_token = IntPtr.Zero;
bool logonSuccessfull = LogonUser(_username, _domain, _password, LogonType.NewCredentials, LogonProvider.WinNT50, ref _token);
if (!logonSuccessfull)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
WindowsIdentity identity = new WindowsIdentity(_token);
_context = identity.Impersonate();
Debug.WriteLine(WindowsIdentity.GetCurrent().Name);
}
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Leave()
{
if (!IsInContext)
return;
_context.Undo();
if (_token != IntPtr.Zero)
{
CloseHandle(_token);
}
_context = null;
}
}
用法:
var impersonationContext = new WrappedImpersonationContext(Domain, Username, Password);
impersonationContext.Enter();
//do your stuff here
impersonationContext.Leave();
对于VB.NET爱好者来说,VB.NET相当于Luke Quinane的代码(谢谢Luke!)
导入系统
导入系统.Net
导入System.Runtime.InteropServices
导入System.ComponentModel
公共类网络连接
实现IDisposable
专用网络名称作为字符串
Public Sub New(网络名称为字符串,凭据为网络凭据)
_networkName=networkName
Dim netResource=新的netResource(){
.Scope=ResourceScope.GlobalNetwork,
.ResourceType=ResourceType.Disk,
.DisplayType=ResourceDisplaytype.Share,
.RemoteName=网络名称
}
Dim userName=If(String.IsNullOrEmpty(credentials.Domain)、credentials.userName、String.Format(“{0}\{1}”、credentials.Domain、credentials.userName))
Dim结果=WNetAddConnection2(网络资源、凭据、密码、用户名、0)
如果结果为0,则
抛出新的Win32Exception(结果,“连接到远程共享时出错”)
如果结束
端接头
受保护的覆盖子完成()
尝试
处置(假)
最后
MyBase.Finalize()
结束尝试
端接头
Public Sub Dispose()实现IDisposable.Dispose
处置(真实)
Imports System
Imports System.Net
Imports System.Runtime.InteropServices
Imports System.ComponentModel
Public Class NetworkConnection
Implements IDisposable
Private _networkName As String
Public Sub New(networkName As String, credentials As NetworkCredential)
_networkName = networkName
Dim netResource = New NetResource() With {
.Scope = ResourceScope.GlobalNetwork,
.ResourceType = ResourceType.Disk,
.DisplayType = ResourceDisplaytype.Share,
.RemoteName = networkName
}
Dim userName = If(String.IsNullOrEmpty(credentials.Domain), credentials.UserName, String.Format("{0}\{1}", credentials.Domain, credentials.UserName))
Dim result = WNetAddConnection2(NetResource, credentials.Password, userName, 0)
If result <> 0 Then
Throw New Win32Exception(result, "Error connecting to remote share")
End If
End Sub
Protected Overrides Sub Finalize()
Try
Dispose (False)
Finally
MyBase.Finalize()
End Try
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose (True)
GC.SuppressFinalize (Me)
End Sub
Protected Overridable Sub Dispose(disposing As Boolean)
WNetCancelConnection2(_networkName, 0, True)
End Sub
<DllImport("mpr.dll")> _
Private Shared Function WNetAddConnection2(netResource As NetResource, password As String, username As String, flags As Integer) As Integer
End Function
<DllImport("mpr.dll")> _
Private Shared Function WNetCancelConnection2(name As String, flags As Integer, force As Boolean) As Integer
End Function
End Class
<StructLayout(LayoutKind.Sequential)> _
Public Class NetResource
Public Scope As ResourceScope
Public ResourceType As ResourceType
Public DisplayType As ResourceDisplaytype
Public Usage As Integer
Public LocalName As String
Public RemoteName As String
Public Comment As String
Public Provider As String
End Class
Public Enum ResourceScope As Integer
Connected = 1
GlobalNetwork
Remembered
Recent
Context
End Enum
Public Enum ResourceType As Integer
Any = 0
Disk = 1
Print = 2
Reserved = 8
End Enum
Public Enum ResourceDisplaytype As Integer
Generic = &H0
Domain = &H1
Server = &H2
Share = &H3
File = &H4
Group = &H5
Network = &H6
Root = &H7
Shareadmin = &H8
Directory = &H9
Tree = &HA
Ndscontainer = &HB
End Enum
module NetworkShare
open System
open System.ComponentModel
open System.IO
open System.Net
open System.Runtime.InteropServices
type ResourceScope =
| Connected = 1
| GlobalNetwork = 2
| Remembered = 3
| Recent = 4
type ResourceType =
| Any = 0
| Disk = 1
| Print = 2
| Reserved = 8
type ResourceDisplayType =
| Generic = 0x0
| Domain = 0x01
| Server = 0x02
| Share = 0x03
| File = 0x04
| Group = 0x05
| Network = 0x06
| Root = 0x07
| Shareadmin = 0x08
| Directory = 0x09
| Tree = 0x0a
| Ndscontainer = 0x0b
//Uses of this construct may result in the generation of unverifiable .NET IL code.
#nowarn "9"
[<StructLayout(LayoutKind.Sequential)>]
type NetResource =
struct
val mutable Scope : ResourceScope
val mutable ResourceType : ResourceType
val mutable DisplayType : ResourceDisplayType
val mutable Usage : int
val mutable LocalName : string
val mutable RemoteName : string
val mutable Comment : string
val mutable Provider : string
new(name) = {
// lets preset needed fields
NetResource.Scope = ResourceScope.GlobalNetwork
ResourceType = ResourceType.Disk
DisplayType = ResourceDisplayType.Share
Usage = 0
LocalName = null
RemoteName = name
Comment = null
Provider = null
}
end
type WNetConnection(networkName : string, credential : NetworkCredential) =
[<Literal>]
static let Mpr = "mpr.dll"
[<DllImport(Mpr, EntryPoint = "WNetAddConnection2")>]
static extern int connect(NetResource netResource, string password, string username, int flags)
[<DllImport(Mpr, EntryPoint = "WNetCancelConnection2")>]
static extern int disconnect(string name, int flags, bool force)
let mutable disposed = false;
do
let userName = if String.IsNullOrWhiteSpace credential.Domain
then credential.UserName
else credential.Domain + "\\" + credential.UserName
let resource = new NetResource(networkName)
let result = connect(resource, credential.Password, userName, 0)
if result <> 0 then
let msg = "Error connecting to remote share " + networkName
new Win32Exception(result, msg)
|> raise
let cleanup(disposing:bool) =
if not disposed then
disposed <- true
if disposing then () // TODO dispose managed resources here
disconnect(networkName, 0, true) |> ignore
interface IDisposable with
member __.Dispose() =
disconnect(networkName, 0, true) |> ignore
GC.SuppressFinalize(__)
override __.Finalize() = cleanup(false)
type CopyPath =
| RemotePath of string * NetworkCredential
| LocalPath of string
let createDisposable() =
{
new IDisposable with
member __.Dispose() = ()
}
let copyFile overwrite destPath srcPath : unit =
use _srcConn =
match srcPath with
| RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable
| LocalPath(_) -> createDisposable()
use _destConn =
match destPath with
| RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable
| LocalPath(_) -> createDisposable()
match srcPath, destPath with
| RemotePath(src, _), RemotePath(dest, _)
| LocalPath(src), RemotePath(dest, _)
| RemotePath(src, _), LocalPath(dest)
| LocalPath(src), LocalPath(dest) ->
if FileInfo(src).Exists |> not then
failwith ("Source file not found: " + src)
let destFilePath =
if DirectoryInfo(dest).Exists then Path.Combine(dest, Path.GetFileName src)
else dest
File.Copy(src, destFilePath, overwrite)
let rec copyDir copySubDirs filePattern destPath srcPath =
use _srcConn =
match srcPath with
| RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable
| LocalPath(_) -> createDisposable()
use _destConn =
match destPath with
| RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable
| LocalPath(_) -> createDisposable()
match srcPath, destPath with
| RemotePath(src, _), RemotePath(dest, _)
| LocalPath(src), RemotePath(dest, _)
| RemotePath(src, _), LocalPath(dest)
| LocalPath(src), LocalPath(dest) ->
let dir = DirectoryInfo(src)
if dir.Exists |> not then
failwith ("Source directory not found: " + src)
let dirs = dir.GetDirectories()
if Directory.Exists(dest) |> not then
Directory.CreateDirectory(dest) |> ignore
let files = dir.GetFiles(filePattern)
for file in files do
let tempPath = Path.Combine(dest, file.Name)
file.CopyTo(tempPath, false) |> ignore
if copySubDirs then
for subdir in dirs do
let subdirSrc =
match srcPath with
| RemotePath(_, credential) -> RemotePath(Path.Combine(dest, subdir.Name), credential)
| LocalPath(_) -> LocalPath(Path.Combine(dest, subdir.Name))
let subdirDest =
match destPath with
| RemotePath(_, credential) -> RemotePath(subdir.FullName, credential)
| LocalPath(_) -> LocalPath(subdir.FullName)
copyDir copySubDirs filePattern subdirDest subdirSrc
ImpersonationHelper.Impersonate(domain, userName, userPassword, delegate
{
//Your code here
//Let's say file copy:
if (!File.Exists(to))
{
File.Copy(from, to);
}
});
using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using Microsoft.Win32.SafeHandles;
namespace BlaBla
{
public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle()
: base(true)
{
}
[DllImport("kernel32.dll")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
}
public class ImpersonationHelper
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private extern static bool CloseHandle(IntPtr handle);
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public static void Impersonate(string domainName, string userName, string userPassword, Action actionToExecute)
{
SafeTokenHandle safeTokenHandle;
try
{
const int LOGON32_PROVIDER_DEFAULT = 0;
//This parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(userName, domainName, userPassword,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
out safeTokenHandle);
//Facade.Instance.Trace("LogonUser called.");
if (returnValue == false)
{
int ret = Marshal.GetLastWin32Error();
//Facade.Instance.Trace($"LogonUser failed with error code : {ret}");
throw new System.ComponentModel.Win32Exception(ret);
}
using (safeTokenHandle)
{
//Facade.Instance.Trace($"Value of Windows NT token: {safeTokenHandle}");
//Facade.Instance.Trace($"Before impersonation: {WindowsIdentity.GetCurrent().Name}");
// Use the token handle returned by LogonUser.
using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle()))
{
using (WindowsImpersonationContext impersonatedUser = newId.Impersonate())
{
//Facade.Instance.Trace($"After impersonation: {WindowsIdentity.GetCurrent().Name}");
//Facade.Instance.Trace("Start executing an action");
actionToExecute();
//Facade.Instance.Trace("Finished executing an action");
}
}
//Facade.Instance.Trace($"After closing the context: {WindowsIdentity.GetCurrent().Name}");
}
}
catch (Exception ex)
{
//Facade.Instance.Trace("Oh no! Impersonate method failed.");
//ex.HandleException();
//On purpose: we want to notify a caller about the issue /Pavel Kovalev 9/16/2016 2:15:23 PM)/
throw;
}
}
}
}