Azure SQL这种PaaS数据库已经越来越被大型企业所接受,对于连接PaaS数据库的方式也和传统的用户名pwd相比有非常大的变化,对于PaaS数据库来说,一方面可以使用传统的数据库userid和pwd登陆,在connection string中注明ID和pwd,也可以使用一些其他形式的身份验证方式,比如service principal和managed identity,这两种方式也越来越被用户所接受

这次要介绍的就是managed identity和Azure SQL数据库的结合,demo的环境主要使用基于.NET Core的app service,以及一个小型的Azure SQL数据库

managed identity的介绍不多说了,简单理解就是个微软托管的用户账户,直接通过后台API自动获取/refresh token,无需用户自己维护pwd,所以通过managed identity访问Azure资源的时候是不需要用户输入pwd的,这样其实也可以一定程度避免身份泄露

不过对于Azure SQL来说,使用managed identity连接到数据库对于代码来说并不是透明的,需要自己引入一些包,并且编写一些code,但是对于开发来说,其实也都是分分钟的事

下边来大概演示下

首先可以自己创建个ASP.NET Core的application,或者github上找个简单的sample也行,在项目中添加下相关包的引入

Install-Package Microsoft.Data.SqlClient -Version 3.0.1
Install-Package Azure.Identity -Version 1.4.0

使用Managed Identity实现无密连接数据库_数据库

在application.json里加上一个connection string

"ConnectionStrings": {
"MyDbConnection": "Server=tcp:mxydemo.database.windows.net;Authentication=Active Directory Device Code Flow; Database=demo;"
}

使用Managed Identity实现无密连接数据库_Azure_02

接下来需要定义一个类,用于处理token等操作,然后在入口的代码里进行初始化,拿到相关的token

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Azure.Core;
using Azure.Identity;
using Microsoft.Data.SqlClient;

namespace DotNetCoreSqlDb.Data
{
public class CustomAzureSQLAuthProvider : SqlAuthenticationProvider
{
private static readonly string[] _azureSqlScopes = new[]
{
"https://database.windows.net//.default"
};

private static readonly TokenCredential _credential = new DefaultAzureCredential();

public override async Task<SqlAuthenticationToken> AcquireTokenAsync(SqlAuthenticationParameters parameters)
{
var tokenRequestContext = new TokenRequestContext(_azureSqlScopes);
var tokenResult = await _credential.GetTokenAsync(tokenRequestContext, default);
return new SqlAuthenticationToken(tokenResult.Token, tokenResult.ExpiresOn);
}

public override bool IsSupported(SqlAuthenticationMethod authenticationMethod) => authenticationMethod.Equals(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow);
}
}

代码相关的内容其实就这么多,本身是很固定的内容,和业务代码完全无关

之后要准备为app service开启managed identity,并且在数据库中针对这个identity进行授权,这里直接通过CLI开启identity

az webapp identity assign --resource-group myResourceGroup --name <app-name>

接下来,使用Azure AD账号连接到SQL Server,在db level创建user,然后授权,这一步是必须要做的,否则managed identity无法通过AAD拿到操作数据库的权限,另外注意这步必须通过AAD账号登陆才可以,sql账号登陆是没办法成功执行下边的命令的,identity-name一般就是app service的名字

CREATE USER [<identity-name>] FROM EXTERNAL PROVIDER;
ALTER ROLE db_datareader ADD MEMBER [<identity-name>];
ALTER ROLE db_datawriter ADD MEMBER [<identity-name>];
ALTER ROLE db_ddladmin ADD MEMBER [<identity-name>];
GO

使用Managed Identity实现无密连接数据库_云_03


之后可以通过git或者FTP等方式将代码发布到app service,如果遇到报错,可以看下project file,将需要的引用加入进去

<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>


<ItemGroup>
<PackageReference Include="Azure.Core" Version="1.22.0" />
<PackageReference Include="Azure.Identity" Version="1.4.0" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="3.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.10">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="5.0.10" />
</ItemGroup>

</Project>

如果遇到Invalid value for key 'authentication',可能是Microsoft.Data.SqlClient版本过低,升级到3.0.x以上再试试

使用Managed Identity实现无密连接数据库_云_04


问题解决之后一般就可以正常访问应用了,在数据库中其实可以很明显看到当前连接是使用的什么provider

使用Managed Identity实现无密连接数据库_数据库_05

可以看到基本不需要配置azure sql的pwd,即使代码公布出去,也不需要担心权限泄露