.NET에서 사용자를 가장하는 간단한 방법이 있습니까?
지금까지 나는 모든 가장 요구 사항에 대해 코드 프로젝트 에서이 클래스를 사용 했습니다 .
.NET Framework를 사용하여 더 좋은 방법이 있습니까?
가장해야 할 자격 증명을 나타내는 사용자 자격 증명 세트 (사용자 이름, 암호, 도메인 이름)가 있습니다.
.NET에서 사용자를 가장하는 간단한 방법이 있습니까?
지금까지 나는 모든 가장 요구 사항에 대해 코드 프로젝트 에서이 클래스를 사용 했습니다 .
.NET Framework를 사용하여 더 좋은 방법이 있습니까?
가장해야 할 자격 증명을 나타내는 사용자 자격 증명 세트 (사용자 이름, 암호, 도메인 이름)가 있습니다.
답변:
.NET 공간에서 "명의 가장"은 일반적으로 특정 사용자 계정으로 코드를 실행하는 것을 의미합니다. 이 두 아이디어는 자주 짝을 이루어도 사용자 이름과 비밀번호를 통해 해당 사용자 계정에 액세스하는 것과는 별개의 개념입니다. 둘 다 설명 하고 내부적으로 사용하는 SimpleImpersonation 라이브러리 를 사용하는 방법을 설명합니다 .
가장을위한 API는 System.Security.Principal
네임 스페이스 를 통해 .NET에서 제공됩니다 .
최신 코드 (.NET 4.6+, .NET Core 등)는 일반적으로을 사용해야합니다. WindowsIdentity.RunImpersonated
이 경우 사용자 계정의 토큰에 대한 핸들을 허용 한 다음 Action
또는 Func<T>
코드가 실행될 수 있습니다.
WindowsIdentity.RunImpersonated(tokenHandle, () =>
{
// do whatever you want as this user.
});
또는
var result = WindowsIdentity.RunImpersonated(tokenHandle, () =>
{
// do whatever you want as this user.
return result;
});
이전 코드는이 WindowsIdentity.Impersonate
메소드를 사용하여 WindowsImpersonationContext
객체 를 검색했습니다 . 이 객체는를 구현 IDisposable
하므로 일반적으로 using
블록 에서 호출해야합니다 .
using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(tokenHandle))
{
// do whatever you want as this user.
}
이 API는 여전히 .NET Framework에 있지만 일반적으로 사용하지 말아야하며 .NET Core 또는 .NET Standard에서는 사용할 수 없습니다.
Windows에서 사용자 계정에 액세스하기 위해 사용자 이름과 비밀번호를 사용하기위한 LogonUser
API는 Win32 기본 API입니다. 현재이를 호출하기위한 기본 제공 .NET API가 없으므로 P / Invoke를 사용해야합니다.
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);
이것이 기본 호출 정의이지만 실제로 프로덕션에서 사용하는 것을 고려해야 할 것이 더 많습니다.
SecureString
사용자 키 입력을 통해 안전하게 수집 할 수있을 때 전달 합니다.이 모든 것을 설명하기 위해 작성해야 할 코드의 양은 IMHO.
이 모든 것을 직접 작성하는 대신 가장과 사용자 액세스를 단일 API로 결합한 SimpleImpersonation 라이브러리를 사용해보십시오 . 동일한 간단한 API를 사용하여 최신 및 기존 코드 기반에서 모두 잘 작동합니다.
var credentials = new UserCredentials(domain, username, password);
Impersonation.RunAsUser(credentials, logonType, () =>
{
// do whatever you want as this user.
});
또는
var credentials = new UserCredentials(domain, username, password);
var result = Impersonation.RunAsUser(credentials, logonType, () =>
{
// do whatever you want as this user.
return something;
});
WindowsIdentity.RunImpersonated
API 와 매우 유사 하지만 토큰 핸들에 대해 알 필요가 없습니다.
이것은 버전 3.0.0부터 API입니다. 자세한 내용은 프로젝트 추가 정보를 참조하십시오. 또한 이전 버전의 라이브러리는 IDisposable
패턴과 유사한 API를 사용했습니다 WindowsIdentity.Impersonate
. 최신 버전이 훨씬 안전하며 둘 다 여전히 내부적으로 사용됩니다.
Undo
폐기 중에 호출되는 것을 명확하게 보여줍니다 .
이것은 아마도 당신이 원하는 것입니다 :
using System.Security.Principal;
using(WindowsIdentity.GetCurrent().Impersonate())
{
//your code goes here
}
하지만 도움이 되려면 더 자세한 정보가 필요합니다. 구성 파일 (웹 사이트 에서이 작업을 수행하려는 경우) 또는 WCF 서비스 인 경우 메소드 데코레이터 (속성)를 통해 가장을 수행하거나 아이디어를 얻을 수 있습니다.
또한 특정 서비스 (또는 웹앱)를 호출 한 클라이언트를 가장하는 경우 적절한 토큰을 전달하도록 클라이언트를 올바르게 구성해야합니다.
마지막으로 실제로 원하는 작업이 위임인 경우 사용자와 컴퓨터가 위임 할 수 있도록 AD를 올바르게 설정해야합니다.
편집 :
봐 여기에 다른 사용자를 가장하는 방법을 참조 추가 문서를 위해 할 수 있습니다.
다음은 Matt Johnson의 답변에 대한 vb.net 포트입니다. 로그온 유형에 대한 열거 형을 추가했습니다. LOGON32_LOGON_INTERACTIVE
SQL Server에서 작동 한 첫 번째 열거 형 값이었습니다. 내 연결 문자열을 신뢰했습니다. 연결 문자열에 사용자 이름 / 비밀번호가 없습니다.
<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _
Public Class Impersonation
Implements IDisposable
Public Enum LogonTypes
''' <summary>
''' This logon type is intended for users who will be interactively using the computer, such as a user being logged on
''' by a terminal server, remote shell, or similar process.
''' This logon type has the additional expense of caching logon information for disconnected operations;
''' therefore, it is inappropriate for some client/server applications,
''' such as a mail server.
''' </summary>
LOGON32_LOGON_INTERACTIVE = 2
''' <summary>
''' This logon type is intended for high performance servers to authenticate plaintext passwords.
''' The LogonUser function does not cache credentials for this logon type.
''' </summary>
LOGON32_LOGON_NETWORK = 3
''' <summary>
''' This logon type is intended for batch servers, where processes may be executing on behalf of a user without
''' their direct intervention. This type is also for higher performance servers that process many plaintext
''' authentication attempts at a time, such as mail or Web servers.
''' The LogonUser function does not cache credentials for this logon type.
''' </summary>
LOGON32_LOGON_BATCH = 4
''' <summary>
''' Indicates a service-type logon. The account provided must have the service privilege enabled.
''' </summary>
LOGON32_LOGON_SERVICE = 5
''' <summary>
''' This logon type is for GINA DLLs that log on users who will be interactively using the computer.
''' This logon type can generate a unique audit record that shows when the workstation was unlocked.
''' </summary>
LOGON32_LOGON_UNLOCK = 7
''' <summary>
''' This logon type preserves the name and password in the authentication package, which allows the server to make
''' connections to other network servers while impersonating the client. A server can accept plaintext credentials
''' from a client, call LogonUser, verify that the user can access the system across the network, and still
''' communicate with other servers.
''' NOTE: Windows NT: This value is not supported.
''' </summary>
LOGON32_LOGON_NETWORK_CLEARTEXT = 8
''' <summary>
''' This logon type allows the caller to clone its current token and specify new credentials for outbound connections.
''' The new logon session has the same local identifier but uses different credentials for other network connections.
''' NOTE: This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider.
''' NOTE: Windows NT: This value is not supported.
''' </summary>
LOGON32_LOGON_NEW_CREDENTIALS = 9
End Enum
<DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)> _
Private Shared Function LogonUser(lpszUsername As [String], lpszDomain As [String], lpszPassword As [String], dwLogonType As Integer, dwLogonProvider As Integer, ByRef phToken As SafeTokenHandle) As Boolean
End Function
Public Sub New(Domain As String, UserName As String, Password As String, Optional LogonType As LogonTypes = LogonTypes.LOGON32_LOGON_INTERACTIVE)
Dim ok = LogonUser(UserName, Domain, Password, LogonType, 0, _SafeTokenHandle)
If Not ok Then
Dim errorCode = Marshal.GetLastWin32Error()
Throw New ApplicationException(String.Format("Could not impersonate the elevated user. LogonUser returned error code {0}.", errorCode))
End If
WindowsImpersonationContext = WindowsIdentity.Impersonate(_SafeTokenHandle.DangerousGetHandle())
End Sub
Private ReadOnly _SafeTokenHandle As New SafeTokenHandle
Private ReadOnly WindowsImpersonationContext As WindowsImpersonationContext
Public Sub Dispose() Implements System.IDisposable.Dispose
Me.WindowsImpersonationContext.Dispose()
Me._SafeTokenHandle.Dispose()
End Sub
Public NotInheritable Class SafeTokenHandle
Inherits SafeHandleZeroOrMinusOneIsInvalid
<DllImport("kernel32.dll")> _
<ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)> _
<SuppressUnmanagedCodeSecurity()> _
Private Shared Function CloseHandle(handle As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Public Sub New()
MyBase.New(True)
End Sub
Protected Overrides Function ReleaseHandle() As Boolean
Return CloseHandle(handle)
End Function
End Class
End Class
Using
가장을 실행하기 위해 일부 코드를 포함 하려면 명령문 과 함께 사용해야 합니다.
이전 답변에서 더 자세한 내용보기 nuget 패키지를 만들었습니다.
Github의 코드
샘플 : 사용 가능 :
string login = "";
string domain = "";
string password = "";
using (UserImpersonation user = new UserImpersonation(login, domain, password))
{
if (user.ImpersonateValidUser())
{
File.WriteAllText("test.txt", "your text");
Console.WriteLine("File writed");
}
else
{
Console.WriteLine("User not connected");
}
}
전체 코드를 Vieuw :
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
/// <summary>
/// Object to change the user authticated
/// </summary>
public class UserImpersonation : IDisposable
{
/// <summary>
/// Logon method (check athetification) from advapi32.dll
/// </summary>
/// <param name="lpszUserName"></param>
/// <param name="lpszDomain"></param>
/// <param name="lpszPassword"></param>
/// <param name="dwLogonType"></param>
/// <param name="dwLogonProvider"></param>
/// <param name="phToken"></param>
/// <returns></returns>
[DllImport("advapi32.dll")]
private static extern bool LogonUser(String lpszUserName,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
/// <summary>
/// Close
/// </summary>
/// <param name="handle"></param>
/// <returns></returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
private WindowsImpersonationContext _windowsImpersonationContext;
private IntPtr _tokenHandle;
private string _userName;
private string _domain;
private string _passWord;
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
/// <summary>
/// Initialize a UserImpersonation
/// </summary>
/// <param name="userName"></param>
/// <param name="domain"></param>
/// <param name="passWord"></param>
public UserImpersonation(string userName, string domain, string passWord)
{
_userName = userName;
_domain = domain;
_passWord = passWord;
}
/// <summary>
/// Valiate the user inforamtion
/// </summary>
/// <returns></returns>
public bool ImpersonateValidUser()
{
bool returnValue = LogonUser(_userName, _domain, _passWord,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
ref _tokenHandle);
if (false == returnValue)
{
return false;
}
WindowsIdentity newId = new WindowsIdentity(_tokenHandle);
_windowsImpersonationContext = newId.Impersonate();
return true;
}
#region IDisposable Members
/// <summary>
/// Dispose the UserImpersonation connection
/// </summary>
public void Dispose()
{
if (_windowsImpersonationContext != null)
_windowsImpersonationContext.Undo();
if (_tokenHandle != IntPtr.Zero)
CloseHandle(_tokenHandle);
}
#endregion
}
나는 파티에 꽤 늦었다는 것을 알고 있지만 필립 앨런 하딩 (Phillip Allan-Harding) 의 도서관은 이 사건과 비슷한 도서관 에 가장 적합하다고 생각합니다.
다음과 같은 작은 코드 조각 만 있으면됩니다.
private const string LOGIN = "mamy";
private const string DOMAIN = "mongo";
private const string PASSWORD = "HelloMongo2017";
private void DBConnection()
{
using (Impersonator user = new Impersonator(LOGIN, DOMAIN, PASSWORD, LogonType.LOGON32_LOGON_NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_WINNT50))
{
}
}
그리고 그의 수업을 추가하십시오 :
가장 된 로그온에 네트워크 자격 증명이 필요하지만 더 많은 옵션이있는 경우이 예를 사용할 수 있습니다.
이 솔루션을 사용할 수 있습니다. (nuget 패키지 사용) 소스 코드는 다음 위치에서 사용할 수 있습니다 : Github : https://github.com/michelcedric/UserImpersonation
자세한 내용은 https://michelcedric.wordpress.com/2015/09/03/usurpation-didentite-dun-user-c-user-impersonation/