分享方式:


Web API Helper 程式碼:驗證類別

 

發佈日期: 2017年1月

適用對象: Dynamics 365 (online)、Dynamics 365 (on-premises)、Dynamics CRM 2016、Dynamics CRM Online

使用 Authentication 類別協助建立驗證的連線至 Microsoft Dynamics 365 Web 服務。 此類別支援兩種驗證通訊協定:Windows 驗證適用於 Dynamics 365 內部部署,或 OAuth 2.0 適用於 Dynamics 365 (線上) 或網際網路對向部署 (IFD)。 此類別依靠 Microsoft Azure Active Directory Authentication Library (ADAL) 處理 OAuth 通訊協定。

Authentication 類別位於 Authentication.cs 檔案中,在 CRM SDK Web API Helper 程式庫中。 其設計是搭配 Configuration Helper 類別階層使用,讓您建立安全的連線至 Dynamics 365 服務,透過 System.Net.Http.HttpMessageHandler 類型的物件。 如需詳細資訊,請參閱使用 Microsoft Dynamics 365 Web API Helper 程式庫 (C#)

驗證處理

Authentication 類別用來驗證 Dynamics 365 服務的機制,取決於您隨 Configuration 參數傳遞至建構函式的資訊而定。 它會嘗試建立 HttpMessageHandler 衍生的物件,可用來具現化 System.Net.Http.HttpClient 執行個體,提供與 Dynamics 365 服務的安全、持續的通訊工作階段。

首先執行簡單的探索交握,透過指定的 Dynamics 365 服務,判斷使用的是 OAuth 或 Windows 原生驗證。

  • 如果使用的是 OAuth,則 OAuthMessageHandler 物件會建立,使用交握中發現的驗證授權單位。 此類別衍生自 System.Net.Http.DelegatingHandler,會重新整理每個要求的 OAuth 存取權杖,因此您不必明確管理權杖到期日。

  • 如果使用 Windows 驗證並提供使用者認證,則這些認證會用來建構 HttpClientHandler

  • 如果使用 Windows 驗證,但未提供使用者認證,則會使用預設網路認證建構 HttpClientHandler。

類別階層與成員

下表顯示 Authentication 類別的公用成員。

Dynamics 365 Web API Helper 程式庫-Authentication 類別圖表

驗證類別

屬性:

Authority – 管理 OAuth 驗證的伺服器 URL。

ClientHandlerHttpMessageHandler 衍生的物件,為訊息要求提供網路認證或授權存取權杖。

ContextAuthenticationContext 用於驗證事件。


方法:

AquireToken – 對於 OAuth,針對目前驗證內容傳回 AuthenticationResult,包含重新整理和存取權杖。

Authentication – 初始化此類別的執行個體,使用 Configuration 參數。

DiscoverAuthority – 探索 Dynamics 365 Web 服務的驗證授權單位。


OAuthMessageHandler 類別

此巢狀類別會針對 Dynamics 365 (線上) 與 IFD 部署設定每個已傳送訊息的授權標題。

使用方式

ConfigurationAuthentication 類別的設計是一前一後用來建立安全的連線至目標 Dynamics 365 服務。 首先建立 Configuration 類型的物件,然後做為單一參數傳遞至 Authentication 建構函式。 在成功建立之後,您可以使用 ClientHandler 屬性建構安全、經驗證且持續的 HTTP 用戶端連線至 Dynamics 365 服務。

建立這個大多數 Web API C# 範例使用的作業的常見方式,是使用衍生類別 FileConfiguration 從正確撰寫的應用程式設定檔讀取連線資訊,如下面各行所示範。

FileConfiguration config = new FileConfiguration(null);
Authentication auth = new Authentication(config);
httpClient = new HttpClient(auth.ClientHandler, true);

如需此使用方式的詳細資訊,請參閱 FileConfiguration 連線設定一節。 雖然 Authentication 類別包含數個其他公用屬性和方法,但是這些主要提供來支援建立 ClientHandler 屬性,而且很少直接由大部分用戶端應用程式存取。

類別清單

此類別的最新來源位於 CRM SDK Web API Helper 程式庫 NuGet 套件中。

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security;
using System.Threading.Tasks;

namespace Microsoft.Crm.Sdk.Samples.HelperCode
{
    /// <summary>
    /// Manages user authentication with the Dynamics CRM Web API (OData v4) services. This class uses Microsoft Azure
    /// Active Directory Authentication Library (ADAL) to handle the OAuth 2.0 protocol. 
    /// </summary>
    public class Authentication
    {
        private Configuration _config = null;
        private HttpMessageHandler _clientHandler = null;
        private AuthenticationContext _context = null;
        private string _authority = null;

        #region Constructors
        /// <summary>
        /// Base constructor.
        /// </summary>
        public Authentication() { }

        /// <summary>
        /// Establishes an authentication session for the service.
        /// </summary>
        /// <param name="config">A populated configuration object.</param>
        public Authentication(Configuration config)
            : base()
        {
            if (config == null)
                throw new Exception("Configuration cannot be null.");

            _config = config;

            SetClientHandler();
        }

        /// <summary>
        /// Custom constructor that allows adding an authority determined asynchronously before 
        /// instantiating the Authentication class.
        /// </summary>
        /// <remarks>For a WPF application, first call DiscoverAuthorityAsync(), and then call this
        /// constructor passing in the authority value.</remarks>
        /// <param name="config">A populated configuration object.</param>
        /// <param name="authority">The URL of the authority.</param>
        public Authentication(Configuration config, string authority)
            : base()
        {
            if (config == null)
                throw new Exception("Configuration cannot be null.");

            _config = config;
            Authority = authority;

            SetClientHandler();
        }
        #endregion Constructors

        #region Properties
        /// <summary>
        /// The authentication context.
        /// </summary>
        public AuthenticationContext Context
        {
            get
            { return _context; }

            set
            { _context = value; }
        }

        /// <summary>
        /// The HTTP client message handler.
        /// </summary>
        public HttpMessageHandler ClientHandler
        {
            get
            { return _clientHandler; }

            set
            { _clientHandler = value; }
        }


        /// <summary>
        /// The URL of the authority to be used for authentication.
        /// </summary>
        public string Authority
        {
            get
            {
                if (_authority == null)
                    _authority = DiscoverAuthority(_config.ServiceUrl);

                return _authority;
            }

            set { _authority = value; }
        }
        #endregion Properties

        #region Methods
        /// <summary>
        /// Returns the authentication result for the configured authentication context.
        /// </summary>
        /// <returns>The refreshed access token.</returns>
        /// <remarks>Refresh the access token before every service call to avoid having to manage token expiration.</remarks>
        public AuthenticationResult AcquireToken()
        {
            if (_config != null && (!string.IsNullOrEmpty(_config.Username) && _config.Password != null))
            {
                UserCredential cred = new UserCredential(_config.Username, _config.Password);
                return _context.AcquireToken(_config.ServiceUrl, _config.ClientId, cred);
            }
            return _context.AcquireToken(_config.ServiceUrl, _config.ClientId, new Uri(_config.RedirectUrl),
                PromptBehavior.Auto);
        }

        /// <summary>
        /// Returns the authentication result for the configured authentication context.
        /// </summary>
        /// <param name="username">The username of a CRM system user in the target organization. </param>
        /// <param name="password">The password of a CRM system user in the target organization.</param>
        /// <returns>The authentication result.</returns>
        /// <remarks>Setting the username or password parameters to null results in the user being prompted to
        /// enter log-on credentials. Refresh the access token before every service call to avoid having to manage
        /// token expiration.</remarks>
        public AuthenticationResult AcquireToken(string username, SecureString password)
        {

            try
            {
                if (!string.IsNullOrEmpty(username) && password != null)
                {
                    UserCredential cred = new UserCredential(username, password);
                    return _context.AcquireToken(_config.ServiceUrl, _config.ClientId, cred);
                }
            }
            catch (Exception e)
            {
                throw new Exception("Authentication failed. Verify the configuration values are correct.", e);
            }
            return null;
        }


        /// <summary>
        /// Discover the authentication authority.
        /// </summary>
        /// <returns>The URL of the authentication authority on the specified endpoint address, or an empty string
        /// if the authority cannot be discovered.</returns>
         public static string DiscoverAuthority(string serviceUrl)
        {
            try
            {
                AuthenticationParameters ap = AuthenticationParameters.CreateFromResourceUrlAsync(
                    new Uri(serviceUrl + "api/data/")).Result;

                return ap.Authority;
            }
            catch (HttpRequestException e)
            {
                throw new Exception("An HTTP request exception occurred during authority discovery.", e);
            }
            catch (System.Exception e )
            {
                // This exception ocurrs when the service is not configured for OAuth.
                if( e.HResult == -2146233088 )
                {
                    return String.Empty;
                }
                else
                {
                    throw e;
                }
            }
        }

        /// <summary>
        /// Discover the authentication authority asynchronously.
        /// </summary>
        /// <param name="serviceUrl">The specified endpoint address</param>
        /// <returns>The URL of the authentication authority on the specified endpoint address, or an empty string
        /// if the authority cannot be discovered.</returns>
        public static async Task<string> DiscoverAuthorityAsync(string serviceUrl)
        {
            try
            {
                AuthenticationParameters ap = await AuthenticationParameters.CreateFromResourceUrlAsync(
                    new Uri(serviceUrl + "api/data/"));

                return ap.Authority;
            }
            catch (HttpRequestException e)
            {
                throw new Exception("An HTTP request exception occurred during authority discovery.", e);
            }
            catch (Exception e)
            {
                // These exceptions ocurr when the service is not configured for OAuth.

                // -2147024809 message: Invalid authenticate header format Parameter name: authenticateHeader
                if (e.HResult == -2146233088 || e.HResult == -2147024809)
                {
                    return String.Empty;
                }
                else
                {
                    throw e;
                }
            }
        }

        /// <summary>
        /// Sets the client message handler as appropriate for the type of authentication
        /// in use on the web service endpoint.
        /// </summary>
        private void SetClientHandler()
        {
            // Check the Authority to determine if OAuth authentication is used.
            if (String.IsNullOrEmpty(Authority))
            {
                if (_config.Username != String.Empty)
                {
                    _clientHandler = new HttpClientHandler()
                    { Credentials = new NetworkCredential(_config.Username, _config.Password, _config.Domain) };
                }
                else
                // No username is provided, so try to use the default domain credentials.
                {
                    _clientHandler = new HttpClientHandler()
                    { UseDefaultCredentials = true };
                }
            }
            else
            {
                _clientHandler = new OAuthMessageHandler(this, new HttpClientHandler());
                _context = new AuthenticationContext(Authority, false);
            }
        }
        #endregion Methods

        /// <summary>
        /// Custom HTTP client handler that adds the Authorization header to message requests. This
        /// is required for IFD and Online deployments.
        /// </summary>
        class OAuthMessageHandler : DelegatingHandler
        {
            Authentication _auth = null;

            public OAuthMessageHandler( Authentication auth, HttpMessageHandler innerHandler )
                : base(innerHandler)
            {
                _auth = auth;
            }

            protected override Task<HttpResponseMessage> SendAsync(
                HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
            {
                // It is a best practice to refresh the access token before every message request is sent. Doing so
                // avoids having to check the expiration date/time of the token. This operation is quick.
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _auth.AcquireToken().AccessToken);

                return base.SendAsync(request, cancellationToken);
            }
        }
    }
}

另請參閱

開始使用 Microsoft Dynamics 365 Web API (C#)
在 Visual Studio (C#) 中啟動 Dynamics 365 Web API 專案
使用 Microsoft Dynamics 365 Web API Helper 程式庫 (C#)
Web API Helper 程式碼︰組態類別
Web API Helper 程式碼:CrmHttpResponseException 類別

Microsoft Dynamics 365

© 2017 Microsoft. 著作權所有,並保留一切權利。 著作權