2013-01-15 20 views
1

我有一個Web服務(舊的Web服務,而不是WCF),我使用IIS 7.0與該Web服務進行通信。在IIS 7.0中只啓用Windows身份驗證(甚至禁用匿名)。我需要能夠在進行服務調用時在代碼中指定特定的Windows身份。我發現許多地方,表明這可以在配置文件中以下列方式完成...以編程方式爲Web服務模擬固定的Windows標識

<authentication mode="Windows" /> 
<identity impersonate="true" userName="UserName" password="[email protected]" /> 

但我需要在代碼中做同樣的事情。我相信很多人都在想「你爲什麼要這麼做」。不用長篇大論,最簡單的答案是因爲這些都是我的要求。

這裏是我的代碼看起來像......

HttpTransportBindingElement transport = useHttps ? new HttpsTransportBindingElement() : new HttpTransportBindingElement(); 
transport.ManualAddressing = false; 
transport.MaxBufferPoolSize = 134217728; // 128MB 
transport.MaxReceivedMessageSize = 134217728; // 128MB 
transport.AllowCookies = false; 
transport.AuthenticationScheme = AuthenticationSchemes.Negotiate; 
transport.BypassProxyOnLocal = false; 
transport.DecompressionEnabled = true; 
transport.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard; 
transport.KeepAliveEnabled = true; 
transport.MaxBufferSize = 134217728; // 128MB, 
transport.ProxyAuthenticationScheme = AuthenticationSchemes.Negotiate; 
transport.Realm = ""; 
transport.TransferMode = TransferMode.Buffered; 
transport.UnsafeConnectionNtlmAuthentication = false; 
transport.UseDefaultWebProxy = false; 

TextMessageEncodingBindingElement encoding = new TextMessageEncodingBindingElement 
{ 
    MaxReadPoolSize = 64, 
    MaxWritePoolSize = 16, 
    MessageVersion = MessageVersion.Soap12, 
    WriteEncoding = Encoding.UTF8, 
    ReaderQuotas = new XmlDictionaryReaderQuotas 
    { 
     MaxDepth = 32, 
     MaxStringContentLength = 134217728, // 128MB 
     MaxArrayLength = 134217728, // 128MB 
     MaxBytesPerRead = 4096, 
     MaxNameTableCharCount = 16384 
    } 
}; 

CustomBinding binding = new CustomBinding(); 
binding.Elements.Add(encoding); 
binding.Elements.Add(transport); 

ServicePointManager.Expect100Continue = false; 

generalSoapClient general = new generalSoapClient(binding, new EndpointAddress("http://localhost/site/ws/general.asmx")); 
NetworkCredential iisCredentials = new NetworkCredential("UserName", "[email protected]"); 
general.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation; 
general.ClientCredentials.Windows.ClientCredential = iisCredentials; 
string session = general.CreateDomainUserSessionFromInstance(); 

有一個在客戶端上的config文件中定義什麼。一切都在代碼中配置。

我的Web服務方法看起來是這樣的(一些代碼缺少是不相關的認證)...

[WebMethod(EnableSession = true)] 
[OperationBehavior(Impersonation = ImpersonationOption.Allowed)] 
public string CreateDomainUserSessionFromInstance() 
{ 
    if(HttpContext.Current.User != null && HttpContext.Current.User.Identity.IsAuthenticated) 
    { 
     WindowsIdentityRequest authenticationRequest = new WindowsIdentityRequest(instanceName, HttpContext.Current.User.Identity as WindowsIdentity); 
     response = authManager.Login(authenticationRequest); 
    } 

    return response.SessionContext.SessionToken; 
} 

我在服務器端的web.config看起來像這樣...

<system.web> 
    <authentication mode="Windows" /> 
    <identity impersonate="true" /> 
    <authorization> 
     <!--<allow users="*" />--> 
     <deny users="?" /> 
    </authorization> 
</system.web> 

<customBinding> 
    <binding name="textHttpBinding" receiveTimeout="00:05:00" sendTimeout="00:05:00"> 
     <textMessageEncoding> 
     <readerQuotas maxArrayLength="1024000000" maxStringContentLength="1024000000" /> 
     </textMessageEncoding> 
     <httpTransport maxReceivedMessageSize="1024000000" maxBufferSize="1024000000" authenticationScheme="Negotiate" /> 
    </customBinding> 

當我有<deny users="?" />時,出現以下錯誤...「HTTP請求未經客戶端身份驗證方案」Negotiate「授權。從服務器接收到的身份驗證頭是」「 有人告訴我應該是<allow users="*" />,但是當我這樣做時,我可以進入網絡服務,但HttpContext.Current.User.Identity.IsAuthenticated是虛假的,.Name是空的,我在互聯網上讀到的是它需要deny users="?" />來拒絕匿名訪問。

我是新來的Web服務,所以不幸的是這段代碼大部分是希臘語給我。我們的Web服務最初允許匿名身份驗證,但需求已更改爲需要Windows身份驗證。

我花了好幾天的時間閱讀了大量的網站,試圖正確配置一切,並且似乎找不到合適的組合。
我在做什麼錯?這是簡單的事情還是我離開基地?

+0

原來的IIS身份驗證爲主要的網站是該網站的一個子文件夾不同的,那麼。 Web服務位於子文件夾中,我正在查看主站點IIS身份驗證。子文件夾仍然打開了Anonymous。 **花了兩天的時間,我幾乎把自己從窗外扔出去了! –

回答

1

這裏有一個類我用來冒充:

public class Impersonator : 
    IDisposable 
{ 
    private const int LOGON32_LOGON_NEW_CREDENTIALS = 9; 


    public Impersonator(
     string userName, 
     string domainName, 
     string password) 
    { 
     ImpersonateValidUser(userName, domainName, password); 
    } 


    public void Dispose() 
    { 
     UndoImpersonation(); 
    } 


    #region P/Invoke. 
    // ------------------------------------------------------------------ 

    [DllImport("advapi32.dll", SetLastError=true)] 
    private static extern int LogonUser(
     string lpszUserName, 
     string lpszDomain, 
     string lpszPassword, 
     int dwLogonType, 
     int dwLogonProvider, 
     ref IntPtr phToken); 

    [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] 
    private static extern int DuplicateToken(
     IntPtr hToken, 
     int impersonationLevel, 
     ref IntPtr hNewToken); 

    [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] 
    private static extern bool RevertToSelf(); 

    [DllImport("kernel32.dll", CharSet=CharSet.Auto)] 
    private static extern bool CloseHandle(
     IntPtr handle); 

    private const int LOGON32_LOGON_INTERACTIVE = 2; 
    private const int LOGON32_PROVIDER_DEFAULT = 0; 


    #region Private member. 
    // ------------------------------------------------------------------ 

    /// <summary> 
    /// Does the actual impersonation. 
    /// </summary> 
    /// <param name="userName">The name of the user to act as.</param> 
    /// <param name="domainName">The domain name of the user to act as.</param> 
    /// <param name="password">The password of the user to act as.</param> 
    private void ImpersonateValidUser(
     string userName, 
     string domain, 
     string password) 
    { 
     WindowsIdentity tempWindowsIdentity = null; 
     IntPtr token = IntPtr.Zero; 
     IntPtr tokenDuplicate = IntPtr.Zero; 

     try 
     { 
      if (RevertToSelf()) 
      { 
       if (LogonUser(
        userName, 
        domain, 
        password, 
        LOGON32_LOGON_NEW_CREDENTIALS, 
        LOGON32_PROVIDER_DEFAULT, 
        ref token) != 0) 
       { 
        if (DuplicateToken(token, 2, ref tokenDuplicate) != 0) 
        { 
         tempWindowsIdentity = new WindowsIdentity(tokenDuplicate); 
         impersonationContext = tempWindowsIdentity.Impersonate(); 
        } 
        else 
        { 
         throw new Win32Exception(Marshal.GetLastWin32Error()); 
        } 
       } 
       else 
       { 
        throw new Win32Exception(Marshal.GetLastWin32Error()); 
       } 
      } 
      else 
      { 
       throw new Win32Exception(Marshal.GetLastWin32Error()); 
      } 
     } 
     finally 
     { 
      if (token!= IntPtr.Zero) 
      { 
       CloseHandle(token); 
      } 
      if (tokenDuplicate!=IntPtr.Zero) 
      { 
       CloseHandle(tokenDuplicate); 
      } 
     } 
    } 

    /// <summary> 
    /// Reverts the impersonation. 
    /// </summary> 
    private void UndoImpersonation() 
    { 
     if (impersonationContext.IsNotNull()) 
     { 
      impersonationContext.Undo(); 
     } 
    } 

    private WindowsImpersonationContext impersonationContext = null; 

    // ------------------------------------------------------------------ 
    #endregion 
} 
+0

這似乎忽略了我傳遞給它的用戶名,密碼和域。即使我傳遞無效憑證,它也會使用當前登錄用戶的憑據登錄。 –

+0

另外,你是否使用這個Web服務,似乎必須有一種方法來使用'general.ClientCredentials.Windows.ClientCredential'來指定Web服務調用中的憑證。 –

相關問題