Implementando IMFNetCredentialManager

No método IMFNetCredentialManager::BeginGetCredentials , faça o seguinte.

  1. Se você ainda não tiver um ponteiro IMFNetCredentialCache , chame MFCreateCredentialCache para criar o objeto de cache de credencial. Armazene esse ponteiro.
  2. Chame IMFNetCredentialCache::GetCredential. Defina os sinalizadores no parâmetro dwAuthenticationFlags da seguinte maneira:
    • Se o membro hrOp da estrutura MFNetCredentialManagerGetParam for igual a NS_E_PROXY_ACCESSDENIED, defina o sinalizador MFNET_AUTHENTICATION_PROXY .
    • Se fClearTextPackage for TRUE, defina o sinalizador MFNET_AUTHENTICATION_CLEAR_TEXT .
    • Se fAllowLoggedOnUser for TRUE, defina o sinalizador MFNET_AUTHENTICATION_LOGGED_ON_USER .
  3. O método GetCredential retorna um ponteiro IMFNetCredential e, possivelmente, o sinalizador REQUIRE_PROMPT. Armazene o ponteiro IMFNetCredential .
  4. Se GetCredential não retornar o sinalizador REQUIRE_PROMPT , você terminará. Pule para a etapa 9.
  5. Caso contrário, se GetCredential retornar o sinalizador REQUIRE_PROMPT , você deverá solicitar ao usuário seu nome de usuário e senha.
  6. Se fClearTextPackage for FALSE, criptografe as credenciais.
  7. Chame IMFNetCredential::SetUser e IMFNetCredential::SetPassword para definir o nome e a senha do usuário no objeto de credenciais.
  8. Opcionalmente, chame IMFNetCredentialCache::SetUserOptions para atualizar o objeto de cache de credenciais com as preferências do usuário para armazenar e enviar credenciais.
  9. Invoque o retorno de chamada IMFAsyncCallback chamando MFInvokeCallback.

No método IMFNetCredentialManager::EndGetCredentials , retorne o ponteiro IMFNetCredential obtido no método BeginGetCredentials .

No método IMFNetCredentialManager::SetGood , passe os parâmetros de entrada diretamente para o método IMFNetCredentialCache::SetGood . Isso notifica o cache de credenciais se as credenciais foram aceitas pelo servidor.

Se você precisar solicitar ao usuário (etapa 5) ou criptografar as credenciais (etapa 6), faça isso em um thread de fila de trabalho para não bloquear o pipeline do Microsoft Media Foundation. Chame MFPutWorkItem e execute as etapas restantes dentro do retorno de chamada da fila de trabalho.

Observação

Lembre-se de que BeginGetCredentials pode ser invocado enquanto a fonte de rede está sendo criada. Portanto, se você criar a fonte de rede chamando o método IMFSourceResolver::CreateObjectFromURL síncrono, o thread de chamada poderá ser bloqueado enquanto as credenciais são adquiridas. Portanto, é recomendável usar o método assíncrono IMFSourceResolver::BeginCreateObjectFromURL .

 

Exemplo

Este exemplo mostra um tipo de comportamento que um gerenciador de credenciais poderia fornecer.

Aqui está uma declaração da classe que implementa IMFNetCredentialManager.

class CCredentialManager : public IMFNetCredentialManager, IMFAsyncCallback 
{
    long                    m_cRef;
    IMFNetCredentialCache   *m_pCredentialCache;

public:
    CCredentialManager () : m_cRef(1), m_pCredentialCache(NULL)
    { 
    }
    ~CCredentialManager()
    {
        SafeRelease(&m_pCredentialCache);
    }

    STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
    {
        static const QITAB qit[] = 
        {
            QITABENT(CCredentialManager, IMFNetCredentialManager),
            QITABENT(CCredentialManager, IMFAsyncCallback),
            { 0 }
        };
        return QISearch(this, qit, riid, ppv);
    }      

    STDMETHODIMP_(ULONG) AddRef()
    {
        return InterlockedIncrement(&m_cRef);
    }

    STDMETHODIMP_(ULONG) Release()
    {
        LONG cRef = InterlockedDecrement(&m_cRef);
        if (cRef == 0)
        {
            delete this;
        }
        return cRef;
    }

    STDMETHODIMP BeginGetCredentials(
        MFNetCredentialManagerGetParam* pParam,
        IMFAsyncCallback* pCallback,
        IUnknown* pState
        );

    STDMETHODIMP EndGetCredentials(
        IMFAsyncResult* pResult, 
        IMFNetCredential** ppCred);

    STDMETHODIMP SetGood(IMFNetCredential* pCred, BOOL fGood)
    {
        if (!pCred)
        {
            return E_POINTER;
        }

        return m_pCredentialCache->SetGood(pCred, fGood);
    }


    STDMETHODIMP GetParameters(DWORD* pdwFlags, DWORD* pdwQueue)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP Invoke(IMFAsyncResult* pResult);
};

Para acompanhar o estado da operação BeginGetCredentials , a classe usa o seguinte objeto auxiliar:

// Holds state information for the GetCredentials operation, so that work can 
// be moved to a work-queue thread.

struct CredentialOp : public IUnknown
{
    long                m_cRef;
    IMFNetCredential    *m_pCredential;
    DWORD               m_dwFlags;

    CredentialOp(IMFNetCredential *pCredential) 
        : m_cRef(1), m_dwFlags(0), m_pCredential(pCredential)
    {
        m_pCredential->AddRef();
    }

    ~CredentialOp()
    {
        SafeRelease(&m_pCredential);
    }

    STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
    {
        static const QITAB qit[] = 
        {
            QITABENT(CredentialOp, IUnknown),
            { 0 }
        };
        return QISearch(this, qit, riid, ppv);
    }      

    STDMETHODIMP_(ULONG) AddRef()
    {
        return InterlockedIncrement(&m_cRef);
    }

    STDMETHODIMP_(ULONG) Release()
    {
        LONG cRef = InterlockedDecrement(&m_cRef);
        if (cRef == 0)
        {
            delete this;
        }
        return cRef;
    }
};

O método BeginGetCredentials cria o cache de credenciais e obtém um ponteiro IMFNetCredential . Se o usuário precisar ser solicitado (indicado pelo sinalizador REQUIRE_PROMPT ), o método chamará MFPutWorkItem para enfileirar um novo item de trabalho:

STDMETHODIMP CCredentialManager::BeginGetCredentials(
    MFNetCredentialManagerGetParam* pParam,
    IMFAsyncCallback* pCallback,
    IUnknown* pState
    )
{
    if (!pParam || !pCallback)
    {
        return E_POINTER;
    }

    DWORD dwAuthenticationFlags = 0;
    DWORD dwRequirementFlags = 0;

    if (pParam->hrOp == NS_E_PROXY_ACCESSDENIED)
    {
        dwAuthenticationFlags |= MFNET_AUTHENTICATION_PROXY;
    }

    if (pParam->fAllowLoggedOnUser)
    {
        dwAuthenticationFlags |= MFNET_AUTHENTICATION_LOGGED_ON_USER;
    }

    if (pParam->fClearTextPackage)
    {
        dwAuthenticationFlags |= MFNET_AUTHENTICATION_CLEAR_TEXT;
    }

    IMFNetCredential *pCredential =  NULL;
    IMFAsyncResult* pResult = NULL;

    HRESULT hr = S_OK;

    if (m_pCredentialCache == NULL)
    {
        hr = MFCreateCredentialCache(&m_pCredentialCache);
        if (FAILED(hr))
        {
            goto done;
        }
    }

    hr = m_pCredentialCache->GetCredential(
        pParam->pszUrl, 
        pParam->pszRealm, 
        dwAuthenticationFlags, 
        &pCredential, 
        &dwRequirementFlags
        );

    if (FAILED(hr))
    {
        goto done;
    }

    if( ( dwRequirementFlags & REQUIRE_PROMPT ) == 0 )
    {
        // The credential is good to use. Prompting the user is not required.
        hr = S_OK;
        goto done;
    }

    // The credential requires prompting the user. 
    CredentialOp *pOp = new (std::nothrow) CredentialOp(pCredential);

    if (pOp == NULL)
    {
        hr = E_OUTOFMEMORY;
        goto done;
    }

    // Set flags. Use these to inform the user if the credentials will
    // be sent in plaintext or saved in the credential cache.

    if (pParam->fClearTextPackage)
    {
        // Notify the user that credentials will be sent in plaintext.
        pOp->m_dwFlags |= MFNET_CREDENTIAL_ALLOW_CLEAR_TEXT;
    }

    if(dwRequirementFlags & REQUIRE_SAVE_SELECTED )
    {
        // Credentials will be saved in the cache by default.
        pOp->m_dwFlags |= MFNET_CREDENTIAL_SAVE;
    }

    // NOTE: The application should enable to user to deselect these two flags;
    // for example, through check boxes in the prompt dialog.


    // Now queue the work item.

    hr = MFCreateAsyncResult(pOp, pCallback, pState, &pResult);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_LONG_FUNCTION, this, pResult);

done:
    SafeRelease(&pResult);
    SafeRelease(&pCredential);
    SafeRelease(&pOp);
    return hr;
}

O thread da fila de trabalho chama Invoke, que solicita ao usuário e, em seguida, chama MFInvokeCallback para invocar o ponteiro de retorno de chamada que foi fornecido em BeginGetCredentials.

STDMETHODIMP CCredentialManager::Invoke(IMFAsyncResult* pResult)
{
    IUnknown *pState = NULL;
    IMFAsyncResult *pGetCredentialsResult = NULL;
    IUnknown *pOpState = NULL;

    CredentialOp *pOp = NULL;   // not AddRef'd

    HRESULT hr = pResult->GetState(&pState);

    if (SUCCEEDED(hr))
    {
        hr = pState->QueryInterface(IID_PPV_ARGS(&pGetCredentialsResult));
    }

    if (SUCCEEDED(hr))
    {
        hr = pGetCredentialsResult->GetObject(&pOpState);
    }

    if (SUCCEEDED(hr))
    {
        pOp = static_cast<CredentialOp*>(pOpState);

        // Display a dialog for the user to enter user name and password.
        hr = PromptUserCredentials(pOp);
    }

    if (SUCCEEDED(hr) && m_pCredentialCache)
    {
        // Update with options set by the user.
        hr = m_pCredentialCache->SetUserOptions(
            pOp->m_pCredential, 
            pOp->m_dwFlags
            );
    }

    if (pGetCredentialsResult)
    {
        pGetCredentialsResult->SetStatus(hr);
        MFInvokeCallback(pGetCredentialsResult);
    }

    SafeRelease(&pState);
    SafeRelease(&pGetCredentialsResult);
    SafeRelease(&pOpState);
    return S_OK;
}

O método EndGetCredentials conclui a operação retornando o ponteiro IMFNetCredential para o chamador.

STDMETHODIMP CCredentialManager::EndGetCredentials(
    IMFAsyncResult* pResult, 
    IMFNetCredential** ppCred
    )
{
    if (!pResult || !ppCred)
    {
        return E_POINTER;
    }

    *ppCred = NULL;

    IUnknown *pUnk = NULL;

    // Check the result of the asynchronous operation.
    HRESULT hr = pResult->GetStatus();

    if (FAILED(hr))
    {
        // The operation failed.
        goto done;
    }

    hr = pResult->GetObject(&pUnk);
    if (FAILED(hr))
    {
        goto done;
    }

    CredentialOp *pOp = static_cast<CredentialOp*>(pUnk);

    *ppCred = pOp->m_pCredential;
    pOp->m_pCredential = NULL;

done:
    SafeRelease(&pUnk);
    return hr;
}

Autenticação de Origem de Rede