Как подключиться к базе данных, используя Active Directory Login и MultiFactor Authentication (MFA) - PullRequest
2 голосов
/ 06 марта 2020

Я уже настроил свой Azure SQL Сервер так, что я являюсь администратором сервера, в моей учетной записи также включен MFA. Я пытался следовать этой документации , но в ней ничего не говорится о Active Directory с MFA.

Я могу использовать свою учетную запись и MFA для входа на сервер, используя SQL Management studio

Изначально я пытался (на основе нового SqlAuthenticationMethod Enum ):

SqlConnection con = new SqlConnection("Server=tcp:myapp.database.windows.net;Database=CustomerDB;Authentication=Active Directory Interactive;Encrypt=True;UID=User@User.co.uk"))

Ошибка:

'Не удается найти аутентификацию провайдер для ActiveDirectoryInteractive.

Затем я увидел это о доступе к SQL через Azure приложение Но это не то, что я хочу сделать.

Этот SO вопрос говорит о подключении без провайдера и установке Driver в строке подключения

SqlConnection con = new SqlConnection("DRIVER={ODBC Driver 17 for SQL Server};Server=tcp:myapp.database.windows.net;Database=CustomerDB;Authentication=Active Directory Interactive;Encrypt=True;UID=User@User.co.uk"))

, но я получаю ошибку:

'Ключевое слово не поддерживается:' драйвер '.'

В любом случае можно написать строку подключения, чтобы при попытке подключения появилось окно проверки подлинности Microsoft, чтобы провести пользователя через многофакторную проверку подлинности

Ответы [ 2 ]

2 голосов
/ 06 марта 2020

Чтобы использовать Azure AD аутентификацию, ваша программа C# должна зарегистрироваться как приложение Azure AD. При завершении регистрации приложения создается и отображается идентификатор приложения. Ваша программа должна включать этот идентификатор для подключения. Чтобы зарегистрировать и установить необходимые разрешения для своего приложения, go для портала Azure, выберите Azure Active Directory> Регистрация приложений> Новая регистрация.

enter image description here

После создания регистрации приложения создается и отображается значение идентификатора приложения.

enter image description here

Выберите разрешения API> Добавить разрешение.

enter image description here

Выберите API, используемые моей организацией> введите Azure SQL База данных в поиске> и выберите Azure SQL База данных.

enter image description here

Выберите Делегированные разрешения> user_impersonation> Добавить разрешения.

enter image description here

Это кажется, вы уже установили Azure AD admin для своей Azure SQL базы данных.

Вы также можете добавить пользователя в базу данных с помощью команды SQL Create User. Примером является CREATE USER [] ИЗ ВНЕШНЕГО ПРОВАЙДЕРА. Для получения дополнительной информации см. здесь .

Ниже приведен пример для C#.

using System;

// Reference to Azure AD authentication assembly
using Microsoft.IdentityModel.Clients.ActiveDirectory;

using DA = System.Data;
using SC = System.Data.SqlClient;
using AD = Microsoft.IdentityModel.Clients.ActiveDirectory;
using TX = System.Text;
using TT = System.Threading.Tasks;

namespace ADInteractive5
{
    class Program
    {
        // ASSIGN YOUR VALUES TO THESE STATIC FIELDS !!
        static public string Az_SQLDB_svrName = "<Your SQL DB server>";
        static public string AzureAD_UserID = "<Your User ID>";
        static public string Initial_DatabaseName = "<Your Database>";
        // Some scenarios do not need values for the following two fields:
        static public readonly string ClientApplicationID = "<Your App ID>";
        static public readonly Uri RedirectUri = new Uri("<Your URI>");

        public static void Main(string[] args)
        {
            var provider = new ActiveDirectoryAuthProvider();

            SC.SqlAuthenticationProvider.SetProvider(
                SC.SqlAuthenticationMethod.ActiveDirectoryInteractive,
                //SC.SqlAuthenticationMethod.ActiveDirectoryIntegrated,  // Alternatives.
                //SC.SqlAuthenticationMethod.ActiveDirectoryPassword,
                provider);

            Program.Connection();
        }

        public static void Connection()
        {
            SC.SqlConnectionStringBuilder builder = new SC.SqlConnectionStringBuilder();

            // Program._  static values that you set earlier.
            builder["Data Source"] = Program.Az_SQLDB_svrName;
            builder.UserID = Program.AzureAD_UserID;
            builder["Initial Catalog"] = Program.Initial_DatabaseName;

            // This "Password" is not used with .ActiveDirectoryInteractive.
            //builder["Password"] = "<YOUR PASSWORD HERE>";

            builder["Connect Timeout"] = 15;
            builder["TrustServerCertificate"] = true;
            builder.Pooling = false;

            // Assigned enum value must match the enum given to .SetProvider().
            builder.Authentication = SC.SqlAuthenticationMethod.ActiveDirectoryInteractive;
            SC.SqlConnection sqlConnection = new SC.SqlConnection(builder.ConnectionString);

            SC.SqlCommand cmd = new SC.SqlCommand(
                "SELECT '******** MY QUERY RAN SUCCESSFULLY!! ********';",
                sqlConnection);

            try
            {
                sqlConnection.Open();
                if (sqlConnection.State == DA.ConnectionState.Open)
                {
                    var rdr = cmd.ExecuteReader();
                    var msg = new TX.StringBuilder();
                    while (rdr.Read())
                    {
                        msg.AppendLine(rdr.GetString(0));
                    }
                    Console.WriteLine(msg.ToString());
                    Console.WriteLine(":Success");
                }
                else
                {
                    Console.WriteLine(":Failed");
                }
                sqlConnection.Close();
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Connection failed with the following exception...");
                Console.WriteLine(ex.ToString());
                Console.ResetColor();
            }
        }
    } // EOClass Program.

    /// <summary>
    /// SqlAuthenticationProvider - Is a public class that defines 3 different Azure AD
    /// authentication methods.  The methods are supported in the new .NET 4.7.2.
    ///  . 
    /// 1. Interactive,  2. Integrated,  3. Password
    ///  . 
    /// All 3 authentication methods are based on the Azure
    /// Active Directory Authentication Library (ADAL) managed library.
    /// </summary>
    public class ActiveDirectoryAuthProvider : SC.SqlAuthenticationProvider
    {
        // Program._ more static values that you set!
        private readonly string _clientId = Program.ClientApplicationID;
        private readonly Uri _redirectUri = Program.RedirectUri;

        public override async TT.Task<SC.SqlAuthenticationToken>
            AcquireTokenAsync(SC.SqlAuthenticationParameters parameters)
        {
            AD.AuthenticationContext authContext =
                new AD.AuthenticationContext(parameters.Authority);
            authContext.CorrelationId = parameters.ConnectionId;
            AD.AuthenticationResult result;

            switch (parameters.AuthenticationMethod)
            {
                case SC.SqlAuthenticationMethod.ActiveDirectoryInteractive:
                    Console.WriteLine("In method 'AcquireTokenAsync', case_0 == '.ActiveDirectoryInteractive'.");

                    result = await authContext.AcquireTokenAsync(
                        parameters.Resource,  // "https://database.windows.net/"
                        _clientId,
                        _redirectUri,
                        new AD.PlatformParameters(AD.PromptBehavior.Auto),
                        new AD.UserIdentifier(
                            parameters.UserId,
                            AD.UserIdentifierType.RequiredDisplayableId));
                    break;

                case SC.SqlAuthenticationMethod.ActiveDirectoryIntegrated:
                    Console.WriteLine("In method 'AcquireTokenAsync', case_1 == '.ActiveDirectoryIntegrated'.");

                    result = await authContext.AcquireTokenAsync(
                        parameters.Resource,
                        _clientId,
                        new AD.UserCredential());
                    break;

                case SC.SqlAuthenticationMethod.ActiveDirectoryPassword:
                    Console.WriteLine("In method 'AcquireTokenAsync', case_2 == '.ActiveDirectoryPassword'.");

                    result = await authContext.AcquireTokenAsync(
                        parameters.Resource,
                        _clientId,
                        new AD.UserPasswordCredential(
                            parameters.UserId,
                            parameters.Password));
                    break;

                default: throw new InvalidOperationException();
            }
            return new SC.SqlAuthenticationToken(result.AccessToken, result.ExpiresOn);
        }

        public override bool IsSupported(SC.SqlAuthenticationMethod authenticationMethod)
        {
            return authenticationMethod == SC.SqlAuthenticationMethod.ActiveDirectoryIntegrated
                || authenticationMethod == SC.SqlAuthenticationMethod.ActiveDirectoryInteractive
                || authenticationMethod == SC.SqlAuthenticationMethod.ActiveDirectoryPassword;
        }
    } // EOClass ActiveDirectoryAuthProvider.
} // EONamespace.  End of entire program source code.

В приведенном выше примере используется Microsoft.IdentityModel.Clients. .ActiveDirectory Сборка DLL.

Чтобы установить этот пакет, в Visual Studio выберите «Проект»> «Управление пакетами NuGet». Найдите и установите Microsoft.IdentityModel.Clients.ActiveDirectory.

Начиная с версии. NET Framework версии 4.7.2, enum SqlAuthenticationMethod имеет новое значение: ActiveDirectoryInteractive.

0 голосов
/ 06 марта 2020

Единственный способ найти вход в систему с использованием Active Directory и MFA и кэшировать токен - это использовать @ метод Альберто

Я также нашел другой способ, который запрашивал учетные данные для входа каждый раз, когда нужно использовать эту строку подключения:

OdbcConnection con = new OdbcConnection("Driver={ODBC Driver 17 for SQL Server};SERVER=tcp:myserver.database.windows.net;DATABASE=MyDb;Authentication=ActiveDirectoryInteractive;UID=User@Userco.uk")

Улучшение кода, опубликованного @alberto. Я должен сказать о чем-то столь фундаментальном в современном мире, что это невероятно недокументировано. В любом случае вот улучшенный код Provider.

Этот код также требует от вас нацеливания . Net Framework 4.7.2 или выше

Сначала следуйте коду @ alberto. Я обнаружил, что еще один не упомянутый шаг заключается в том, что вам нужно также настроить Platform для вашего приложения в azure на вкладке аутентификации, чтобы оно выглядело так:

enter image description here

Добавьте эти два класса в свой проект:

ActiveDirectoryAuthProvider

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Data.SqlClient;

namespace SQLAzureConnectivity
{
    public class ActiveDirectoryAuthProvider : SqlAuthenticationProvider
    {
        private string _clientId { get; set; }

        private Uri _redirectURL { get; set; } = new Uri("https://login.microsoftonline.com/common/oauth2/nativeclient");

        public ActiveDirectoryAuthProvider(string clientId)
        {
            _clientId = clientId;

        }

        //https://docs.microsoft.com/en-us/azure/sql-database/active-directory-interactive-connect-azure-sql-db#c-code-example
        public override async Task<System.Data.SqlClient.SqlAuthenticationToken>  AcquireTokenAsync(System.Data.SqlClient.SqlAuthenticationParameters parameters)
        {
            AuthenticationContext authContext = new AuthenticationContext(parameters.Authority, new FilesBasedAdalV3TokenCache(".\\Token.dat"));
            authContext.CorrelationId = parameters.ConnectionId;
            AuthenticationResult result = null;

            switch (parameters.AuthenticationMethod)
            {
                case System.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryInteractive:
                    Console.WriteLine("In method 'AcquireTokenAsync', case_0 == '.ActiveDirectoryInteractive'.");

                    try
                    {
                        result = await authContext.AcquireTokenSilentAsync(parameters.Resource, _clientId);
                    }
                    catch (AdalException adalException)
                    {
                        if (adalException.ErrorCode == AdalError.FailedToAcquireTokenSilently  || adalException.ErrorCode == AdalError.InteractionRequired)
                        {
                            result = await authContext.AcquireTokenAsync(parameters.Resource, _clientId, _redirectURL, new PlatformParameters(PromptBehavior.Auto));
                            //result = await authContext.AcquireTokenAsync(parameters.Resource, _clientId, _redirectURL, new PlatformParameters(PromptBehavior.Auto), new UserIdentifier(parameters.UserId, UserIdentifierType.RequiredDisplayableId));
                        }
                    }
                    break;

                case System.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryIntegrated:
                    Console.WriteLine("In method 'AcquireTokenAsync', case_1 == '.ActiveDirectoryIntegrated'.");
                    result = await authContext.AcquireTokenAsync(parameters.Resource, _clientId, new UserCredential());
                    break;

                case System.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryPassword:
                    Console.WriteLine("In method 'AcquireTokenAsync', case_2 == '.ActiveDirectoryPassword'.");
                    result = await authContext.AcquireTokenAsync(parameters.Resource, _clientId, new UserPasswordCredential(parameters.UserId, parameters.Password));
                    break;

                default: 
                    throw new InvalidOperationException();
            }

            return new System.Data.SqlClient.SqlAuthenticationToken(result.AccessToken, result.ExpiresOn);
        }

        public override bool IsSupported(System.Data.SqlClient.SqlAuthenticationMethod authenticationMethod)
        {
            return authenticationMethod == System.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryIntegrated
                || authenticationMethod == System.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryInteractive
                || authenticationMethod == System.Data.SqlClient.SqlAuthenticationMethod.ActiveDirectoryPassword;
        }
    }
}

FilesBasedAdalV3TokenCache

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.IO;
using System.Security.Cryptography;

namespace SQLAzureConnectivity
{
    // This is a simple persistent cache implementation for an ADAL V3 desktop application
    public class FilesBasedAdalV3TokenCache : TokenCache
    {
        public string CacheFilePath { get; }
        private static readonly object FileLock = new object();

        // Initializes the cache against a local file.
        // If the file is already present, it loads its content in the ADAL cache
        public FilesBasedAdalV3TokenCache(string filePath)
        {
            CacheFilePath = filePath;
            this.AfterAccess = AfterAccessNotification;
            this.BeforeAccess = BeforeAccessNotification;
            lock (FileLock)
            {
                this.DeserializeAdalV3(ReadFromFileIfExists(CacheFilePath));
            }
        }

        // Empties the persistent store.
        public override void Clear()
        {
            base.Clear();
            File.Delete(CacheFilePath);
        }

        // Triggered right before ADAL needs to access the cache.
        // Reload the cache from the persistent store in case it changed since the last access.
        void BeforeAccessNotification(TokenCacheNotificationArgs args)
        {
            lock (FileLock)
            {
                this.DeserializeAdalV3(ReadFromFileIfExists(CacheFilePath));
            }
        }

        // Triggered right after ADAL accessed the cache.
        void AfterAccessNotification(TokenCacheNotificationArgs args)
        {
            // if the access operation resulted in a cache update
            if (this.HasStateChanged)
            {
                lock (FileLock)
                {
                    // reflect changes in the persistent store
                    WriteToFileIfNotNull(CacheFilePath, this.SerializeAdalV3());
                    // once the write operation took place, restore the HasStateChanged bit to false
                    this.HasStateChanged = false;
                }
            }
        }

        /// <summary>
        /// Read the content of a file if it exists
        /// </summary>
        /// <param name="path">File path</param>
        /// <returns>Content of the file (in bytes)</returns>
        private byte[] ReadFromFileIfExists(string path)
        {
            byte[] protectedBytes = (!string.IsNullOrEmpty(path) && File.Exists(path))
                ? File.ReadAllBytes(path) : null;
            byte[] unprotectedBytes = (protectedBytes != null)
                ? ProtectedData.Unprotect(protectedBytes, null, DataProtectionScope.CurrentUser) : null;
            return unprotectedBytes;
        }

        /// <summary>
        /// Writes a blob of bytes to a file. If the blob is <c>null</c>, deletes the file
        /// </summary>
        /// <param name="path">path to the file to write</param>
        /// <param name="blob">Blob of bytes to write</param>
        private static void WriteToFileIfNotNull(string path, byte[] blob)
        {
            if (blob != null)
            {
                byte[] protectedBytes = ProtectedData.Protect(blob, null, DataProtectionScope.CurrentUser);
                File.WriteAllBytes(path, protectedBytes);
            }
            else
            {
                File.Delete(path);
            }
        }
    }
}

Затем перед использованием SQLConnection напишите эти две строки:

var provider = new ActiveDirectoryAuthProvider("ClientID from the Azure app you set up earlier");
SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, provider);

Ссылки:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...