Udostępnij przez


Obsługa uwierzytelniania

Niektóre serwery proxy i serwery wymagają uwierzytelniania przed udzieleniem dostępu do zasobów w Internecie. Funkcje WinINet obsługują uwierzytelnianie serwera i proxy dla sesji HTTP. Uwierzytelnianie serwerów FTP musi być obsługiwane przez funkcję InternetConnect. Obecnie uwierzytelnianie bramy FTP nie jest obsługiwane.

Informacje o uwierzytelnianiu HTTP

Jeśli wymagane jest uwierzytelnianie, aplikacja kliencka otrzymuje kod stanu 401, jeśli serwer wymaga uwierzytelniania lub 407, jeśli serwer proxy wymaga uwierzytelniania. Przy użyciu kodu stanu serwer proxy lub serwer wysyła jeden lub więcej nagłówków odpowiedzi uwierzytelniania —Proxy-Authenticate (na potrzeby uwierzytelniania serwera proxy) lub WWW-Authenticate (na potrzeby uwierzytelniania serwera).

Każdy nagłówek odpowiedzi uwierzytelnienia zawiera dostępny schemat uwierzytelniania i obszar. Jeśli jest obsługiwanych wiele schematów uwierzytelniania, serwer zwraca wiele nagłówków odpowiedzi uwierzytelnienia. Wartość realm jest rozróżniana ze względu na wielkość liter i definiuje przestrzeń ochrony na serwerze proxy lub serwerze. Na przykład nagłówek "WWW-Authentication: Basic Realm="example"" będzie przykładem nagłówka zwracanego, gdy wymagane jest uwierzytelnianie serwera.

Aplikacja kliencka, która wysłała żądanie, może się uwierzytelnić, dołączając pole nagłówka autoryzacji z żądaniem. Nagłówek Autoryzacji zawiera schemat uwierzytelniania i odpowiednią odpowiedź wymaganą przez ten schemat. Na przykład nagłówek "Authorization: Basic <username:password>" zostanie dodany do żądania i ponownie wysłany na serwer, jeśli klient otrzymał nagłówek odpowiedzi uwierzytelnienia "WWW-Authenticate: Basic Realm="example"".

Istnieją dwa ogólne typy schematów uwierzytelniania:

  • Podstawowy schemat uwierzytelniania, w którym nazwa użytkownika i hasło są wysyłane w postaci zwykłego tekstu do serwera.
  • Schematy wyzwań i odpowiedzi, które umożliwiają format wyzwania-odpowiedzi.

Schemat uwierzytelniania podstawowego jest oparty na modelu, w którym klient musi się uwierzytelnić używając nazwy użytkownika i hasła dla każdej domeny. Serwer obsługuje żądanie, jeśli jest ponownie wysyłane z nagłówkiem autoryzacji zawierającym prawidłową nazwę użytkownika i hasło.

Schematy odpowiedzi na wyzwania umożliwiają bezpieczniejsze uwierzytelnianie. Jeśli żądanie wymaga uwierzytelnienia przy użyciu schematu wyzwanie-odpowiedź, do klienta są zwracane odpowiedni kod stanu i nagłówki związane z uwierzytelnieniem. Klient musi następnie ponownie wysłać żądanie z negocjacjami. Serwer zwróci odpowiedni kod stanu z wyzwaniem, a klient będzie musiał ponownie wysłać żądanie z odpowiednią odpowiedzią, aby uzyskać żądaną usługę.

W poniższej tabeli wymieniono schematy uwierzytelniania, typ uwierzytelniania, bibliotekę DLL, która je obsługuje, oraz opis schematu.

Schemat Typ DLL Opis
Podstawowy (tekst jawny) podstawowy Wininet.dll Używa zakodowanego w formacie base64 ciągu zawierającego nazwę użytkownika i hasło.
Podsumowanie odpowiedź na wyzwanie Digest.dll Schemat odpowiedzi na żądanie, który dokonuje wyzwania przy użyciu wartości nonce (ciągu danych określonego przez serwer). Prawidłowa odpowiedź zawiera sumę kontrolną nazwy użytkownika, hasła, danej liczby jednorazowej, metody HTTP i żądanego identyfikatora URI (Uniform Resource Identifier). Obsługa uwierzytelniania typu Digest została wprowadzona w programie Microsoft Internet Explorer 5.
NT LAN Manager (NTLM) odpowiedź na wyzwanie Winsspi.dll Schemat odpowiedzi na żądanie, który opiera wyzwanie na nazwie użytkownika.
Microsoft Network (MSN) wyzwanie-odpowiedź Msnsspc.dll Schemat uwierzytelniania usługi Microsoft Network.
Rozproszone uwierzytelnianie haseł (DPA) mechanizm wyzwanie-odpowiedź Msapsspc.dll Podobnie jak w przypadku uwierzytelniania MSN, a także jest używany przez usługę Microsoft Network.
Zdalne uwierzytelnianie passphrase (RPA) CompuServe Rpawinet.dll, da.dll Schemat uwierzytelniania CompuServe. Aby uzyskać więcej informacji, zobacz Specyfikacje mechanizmu RPA.

 

Dla każdego rodzaju uwierzytelniania innego niż podstawowe należy skonfigurować klucze rejestru oprócz instalacji odpowiedniej biblioteki DLL.

Jeśli wymagane jest uwierzytelnianie, flaga INTERNET_FLAG_KEEP_CONNECTION powinna być używana w wywołaniu do HttpOpenRequest. Flaga INTERNET_FLAG_KEEP_CONNECTION jest wymagana dla protokołu NTLM i innych typów uwierzytelniania w celu utrzymania połączenia podczas kończenia procesu uwierzytelniania. Jeśli połączenie nie jest utrzymywane, proces uwierzytelniania należy ponownie uruchomić za pomocą serwera proxy lub serwera.

Funkcje InternetOpenUrl i HttpSendRequest zakończą się pomyślnie, nawet jeśli jest wymagane uwierzytelnianie. Różnica polega na tym, że dane zwrócone w plikach nagłówków i InternetReadFile otrzymają stronę HTML informującą użytkownika o kodzie stanu.

Rejestrowanie kluczy uwierzytelniania

INTERNET_OPEN_TYPE_PRECONFIG analizuje wartości rejestru ProxyEnable, ProxyServeri ProxyOverride. Te wartości znajdują się w HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings.

W przypadku schematów uwierzytelniania innych niż Podstawowe należy dodać klucz do rejestru w HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Security. Wartość DWORD o nazwie i Flagi powinny być ustawione z odpowiednią wartością. Na poniższej liście przedstawiono możliwe wartości dla wartości Flags.

  • PLUGIN_AUTH_FLAGS_UNIQUE_CONTEXT_PER_TCPIP (value=0x01)

    Każde gniazdo protokołu TCP/IP (Transmission Control Protocol/Internet Protocol) zawiera inny kontekst. W przeciwnym razie dla każdego obszaru lub szablonu URL bloku przekazywany jest nowy kontekst.

  • PLUGIN_AUTH_FLAGS_CAN_HANDLE_UI (wartość=0x02)

    Ta biblioteka DLL może obsługiwać własne dane wejściowe użytkownika.

  • PLUGIN_AUTH_FLAGS_CAN_HANDLE_NO_PASSWD (value=0x04)

    Ta biblioteka DLL może być w stanie wykonać uwierzytelnianie bez monitowania użytkownika o podanie hasła.

  • PLUGIN_AUTH_FLAGS_NO_REALM (value=0x08)

    Ta biblioteka DLL nie używa standardowego ciągu realms HTTP. Wszystkie dane, które wydają się być obszarem, to dane specyficzne dla schematu.

  • PLUGIN_AUTH_FLAGS_KEEP_ALIVE_NOT_REQUIRED (value=0x10)

    Ta biblioteka DLL nie wymaga trwałego połączenia dla sekwencji odpowiedzi na żądanie.

Aby na przykład dodać uwierzytelnianie NTLM, należy dodać klucz NTLM do HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Security. W obszarze HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Security\NTLMnależy dodać wartość ciągu DLLFileoraz wartość DWORDFlagi. DLLFile musi być ustawiona na Winsspi.dll, a flagi muszą być ustawione na 0x08.

Uwierzytelnianie serwera

Gdy serwer odbiera żądanie wymagające uwierzytelniania, serwer zwraca komunikat o kodzie stanu 401. W tym komunikacie serwer powinien zawierać co najmniej jeden nagłówek odpowiedzi WWW-Authenticate. Te nagłówki obejmują metody uwierzytelniania dostępne przez serwer. WinINet wybiera pierwszą rozpoznawaną przez nią metodę.

Uwierzytelnianie podstawowe zapewnia słabe zabezpieczenia, chyba że kanał jest najpierw szyfrowany za pomocą protokołu SSL lub PCT.

Funkcja InternetErrorDlg może służyć do uzyskiwania nazwy użytkownika i danych hasła od użytkownika lub dostosowany interfejs użytkownika można zaprojektować w celu uzyskania danych.

Interfejs niestandardowy może używać funkcji InternetSetOption, aby ustawić wartości INTERNET_OPTION_PASSWORD i INTERNET_OPTION_USERNAME, a następnie ponownie wysłać żądanie do serwera.

Uwierzytelnianie serwera proxy

Gdy klient próbuje użyć serwera proxy wymagającego uwierzytelniania, serwer proxy zwraca do klienta komunikat o kodzie stanu 407. W tym komunikacie serwer proxy powinien umieścić co najmniej jeden Proxy-Authenticate nagłówek odpowiedzi. Te nagłówki obejmują metody uwierzytelniania dostępne z serwera proxy. WinINet wybiera pierwszą rozpoznawaną przez nią metodę.

Funkcja InternetErrorDlg może służyć do uzyskiwania nazwy użytkownika i danych hasła od użytkownika lub można zaprojektować dostosowany interfejs użytkownika.

Interfejs niestandardowy może używać funkcji InternetSetOption, aby ustawić wartości INTERNET_OPTION_PROXY_PASSWORD i INTERNET_OPTION_PROXY_USERNAME, a następnie ponownie wysłać żądanie do serwera proxy.

Jeśli nie ustawiono nazwy użytkownika i hasła serwera proxy, usługa WinINet próbuje użyć nazwy użytkownika i hasła dla serwera. To zachowanie umożliwia klientom zaimplementowanie tego samego dostosowanego interfejsu użytkownika używanego do obsługi uwierzytelniania serwera.

Obsługa uwierzytelniania HTTP

Uwierzytelnianie HTTP można obsługiwać za pomocą InternetErrorDlg lub niestandardowej funkcji, która używa InternetSetOption lub dodaje własne nagłówki uwierzytelniania. InternetErrorDlg może zbadać nagłówki skojarzone z handlem w celu znalezienia ukrytych błędów, takich jak kody stanu z serwera proxy lub serwera. InternetSetOption można ustawić nazwę użytkownika i hasło serwera proxy i serwera. W przypadku uwierzytelniania MSN i DPA InternetErrorDlg należy użyć do ustawienia nazwy użytkownika i hasła.

W przypadku dowolnej dostosowanej funkcji, która dodaje własne nagłówki WWW-Authenticate lub Proxy-Authenticate, należy ustawić flagę INTERNET_FLAG_NO_AUTH, aby wyłączyć uwierzytelnianie.

W poniższym przykładzie pokazano, jak można użyć InternetErrorDlg do obsługi uwierzytelniania HTTP.

HINTERNET hOpenHandle,  hConnectHandle, hResourceHandle;
DWORD dwError, dwErrorCode;
HWND hwnd = GetConsoleWindow();

hOpenHandle = InternetOpen(TEXT("Example"),
                           INTERNET_OPEN_TYPE_PRECONFIG, 
                           NULL, NULL, 0);

hConnectHandle = InternetConnect(hOpenHandle,
                                 TEXT("www.server.com"), 
                                 INTERNET_INVALID_PORT_NUMBER,
                                 NULL,
                                 NULL, 
                                 INTERNET_SERVICE_HTTP,
                                 0,0);

hResourceHandle = HttpOpenRequest(hConnectHandle, TEXT("GET"),
                                  TEXT("/premium/default.htm"),
                                  NULL, NULL, NULL, 
                                  INTERNET_FLAG_KEEP_CONNECTION, 0);

resend:

HttpSendRequest(hResourceHandle, NULL, 0, NULL, 0);

// dwErrorCode stores the error code associated with the call to
// HttpSendRequest.  

dwErrorCode = hResourceHandle ? ERROR_SUCCESS : GetLastError();

dwError = InternetErrorDlg(hwnd, hResourceHandle, dwErrorCode, 
                           FLAGS_ERROR_UI_FILTER_FOR_ERRORS | 
                           FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS |
                           FLAGS_ERROR_UI_FLAGS_GENERATE_DATA,
                           NULL);

if (dwError == ERROR_INTERNET_FORCE_RETRY)
    goto resend;

// Insert code to read the data from the hResourceHandle
// at this point.

W tym przykładzie dwErrorCode służy do przechowywania wszelkich błędów skojarzonych z wywołaniem HttpSendRequest. HttpSendRequest kończy się pomyślnie, nawet jeśli serwer proxy lub inny wymaga uwierzytelnienia. Po przekazaniu flagi FLAGS_ERROR_UI_FILTER_FOR_ERRORS do InternetErrorDlgfunkcja sprawdza nagłówki pod kątem ukrytych błędów. Te ukryte błędy obejmują wszelkie żądania uwierzytelniania. InternetErrorDlg wyświetla odpowiednie okno dialogowe, aby monitować użytkownika o niezbędne dane. Flagi FLAGS_ERROR_UI_FLAGS_GENERATE_DATA i FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS powinny być również przekazywane do InternetErrorDlg, tak aby funkcja mogła zbudować odpowiednią strukturę danych dla błędu i przechować wyniki okna dialogowego w uchwycie HINTERNET.

Poniższy przykładowy kod pokazuje, jak można obsługiwać uwierzytelnianie przy użyciu InternetSetOption.

HINTERNET hOpenHandle,  hResourceHandle, hConnectHandle;
DWORD dwStatus;
DWORD dwStatusSize = sizeof(dwStatus);
char strUsername[64], strPassword[64];

// Normally, hOpenHandle, hResourceHandle,
// and hConnectHandle need to be properly assigned.

hOpenHandle = InternetOpen(TEXT("Example"),
                           INTERNET_OPEN_TYPE_PRECONFIG,
                           NULL, NULL, 0);
hConnectHandle = InternetConnect(hOpenHandle,
                                 TEXT("www.server.com"),
                                 INTERNET_INVALID_PORT_NUMBER,
                                 NULL,
                                 NULL,
                                 INTERNET_SERVICE_HTTP,
                                 0,0);

hResourceHandle = HttpOpenRequest(hConnectHandle, TEXT("GET"),
                                  TEXT("/premium/default.htm"),
                                  NULL, NULL, NULL,
                                  INTERNET_FLAG_KEEP_CONNECTION,
                                  0);

resend:

HttpSendRequest(hResourceHandle, NULL, 0, NULL, 0);

HttpQueryInfo(hResourceHandle, HTTP_QUERY_FLAG_NUMBER |
              HTTP_QUERY_STATUS_CODE, &dwStatus, &dwStatusSize, NULL);

switch (dwStatus)
{
    // cchUserLength is the length of strUsername and
    // cchPasswordLength is the length of strPassword.
    DWORD cchUserLength, cchPasswordLength;

    case HTTP_STATUS_PROXY_AUTH_REQ: // Proxy Authentication Required
        // Insert code to set strUsername and strPassword.

        // Insert code to safely determine cchUserLength and
        // cchPasswordLength. Insert appropriate error handling code.
        InternetSetOption(hResourceHandle,
                          INTERNET_OPTION_PROXY_USERNAME,
                          strUsername,
                          cchUserLength+1);

        InternetSetOption(hResourceHandle,
                          INTERNET_OPTION_PROXY_PASSWORD,
                          strPassword,
                          cchPasswordLength+1);
        goto resend;
        break;

    case HTTP_STATUS_DENIED:     // Server Authentication Required.
        // Insert code to set strUsername and strPassword.

        // Insert code to safely determine cchUserLength and
        // cchPasswordLength. Insert error handling code as
        // appropriate.
        InternetSetOption(hResourceHandle, INTERNET_OPTION_USERNAME,
                          strUsername, cchUserLength+1);
        InternetSetOption(hResourceHandle, INTERNET_OPTION_PASSWORD,
                          strPassword, cchPasswordLength+1);
        goto resend;
        break;
}

// Insert code to read the data from the hResourceHandle
// at this point.

Uwaga

Usługa WinINet nie obsługuje implementacji serwera. Ponadto nie należy jej używać w ramach usługi. W przypadku implementacji serwera lub usług należy użyć usług HTTP systemu Microsoft Windows (WinHTTP).