同一应用程序上的 AAD OpenId 和承载身份验证
AAD OpenId and Bearer Authentication on the same Application
我正在尝试使用 Azure Active Directory 身份验证创建身份验证,我的应用程序由 WebApis 和 WEb 页面组成,我想要实现的是在用户请求应用程序时将用户重定向到 Microsoft 登录页面“控制器”页面,并在通过令牌调用 API 控制器时授权客户端应用程序。
我通过如下设置 Startup.Auth.cs 来做到这一点
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.IdToken,
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
}
});
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters { SaveSigninToken = true, ValidAudience = $"api://{clientId}" }
});
}
用 [Authorize] 属性装饰我的控制器
但是,当未经授权的客户端应用程序调用 API 时,它会返回以下
<html>
<head>
<title>Working...</title>
</head>
<body>
<form method="POST" name="hiddenform" action="https://local:44341/">
<input type="hidden" name="id_token" value="eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Im5PbzNaRHJPRFhFSzFqS1doWHNsSFJfS1hFZyJ9.eyJhdWQiOiI3NjIzMjcyMC1hNmViLTRkOTctOTI3Yy1lMWFjNzRiODIyMjAiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vYjFmZTVlMmEtZjc1Yy00NDQxLTlmYjItNWFjYzAxYmEwMGQwL3YyLjAiLCJpYXQiOjE2MTA3MzAJub25jZSI6IjYzNzQ2MzI3OTEwNDAxMzk3Ni5aR0k1TmpCaU1XWXRPRGsxT0MwMFpEWmlMV0kxWXprdE9ESTFZMlZrTWpjeU0yWTJNRFZqTWpnNU9EY3RNak0yWkMwME1qWmpMV0UwTTJZdE5XSXpNVFZsTlRKa1pHRXgiLCJvaWQiOiJiN2YzMjg1Mi1hMDRhLTRlOWUtYjFjOS02NTI5NDk3YTgyZDEiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiIyMjQxN0Byai5jb20iLCJyaCI6IjAuQUFBQUtsNy1zVnozUVVWoeHm6x2ad02ExHlw48lfRVZhEoHw8cZ5SWRFOGZfi3TJ3aOFKdaDZZ8Yt1no30Hku8pL3Vf7Lrq-BuKX8PNtkU45aVO1fZf8PFdM4UbpYnHOwvuG1NlPG8mTa1KheQTza9j3PTaiLq8e15jtSoFoHfIJbMZJoNTfvAF40kt9XvYee-rga80Oj1tJX78g_80MmRYORwArr1rq1n6EyPgFHaDDF5vD-zWOLDrXKyj2rwW-7LLpbtojtbsyCPdM5QPkLKnZZFanpvwRAFRTHaLdHINGAlHhvhFP9kvRhVtaTUgwYzrFqlN8k3zSZrvnMOec7A" /><input type="hidden" name="state" value="OpenIdConnect.AuthenticationProperties=7_QsmCbQZ3vBxo1tTvZXcYRLeNQMBZfanq5zpZvqNjuudSAu52-UZnVGkkWMXeBh_rIHE3i_j8g_B751WFqQHR1CXJrdjBi6PZy-" /><input type="hidden" name="session_state" value="5938bc609" /><noscript>
<p>Script is disabled. Click Submit to continue.</p><input type="submit" value="Submit" /></noscript></form>
<script language="javascript">
document.forms[0].submit();
</script>
</body>
</html>
但是当从我的函数中删除对 UseOpenIdConnectAuthentication 的调用时,它返回正确的响应消息,状态代码为 401
{ "Message": "Authorization has been denied for this request."}
话虽如此,谁能帮助我了解如何为 Web api 设置不记名令牌身份验证,以及如何为 Web 应用程序页面设置 openIdConnect。
谢谢,如果我的方法不对,请告诉我,并根据我的要求推荐正确的方法。
不要在此处使用 UseOpenIdConnectAuthentication,它用于自定义 OpenID 支持,这在此处并不适用,具有开箱即用的 Azure AD 特定帮助程序,例如您接下来使用的帮助程序。
这是服务器端的一个很好的起点:https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-overview
对于客户端,通用调用者(控制台应用程序或任意服务):https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-daemon-overview
对于客户端,需要自我保护的交互式网络应用程序:https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-web-app-sign-user-overview
简而言之,您的 API 客户端必须向 Azure AD 请求令牌才能访问您的 API 端点。要让 Azure AD 知道此令牌用于哪个资源,请为您的 API 创建应用程序注册,并使用其客户端 ID 和任何其他必要参数配置 API 应用程序。
当您的应用程序收到令牌时,它必须联系 Azure AD 来验证它。对于此调用,它应该使用您在 Azure AD 中为其生成的客户端 ID。
您可能希望在模拟真实的 AD 登录时 运行 客户端,或者您可能 运行 客户端作为某个 AD 主体(应用程序注册)。查看下面的 .NET Core 3.1 客户端应用程序示例是否可以帮助您。它使用其 ID 和密码模拟一些 AD 主体,因此它可以 运行 并从 Azure 云外部连接到受 Azure AD 保护的服务,而无需托管身份。
为了使下面的示例正常工作,请确保正确配置客户端应用程序和服务器 API 的主体(应用程序注册/企业应用程序)。这本身就是一个话题,在上面链接的官方文档中有详细描述。
如果您的客户端 运行 作为 Azure AppService 或在 Azure VM 上,最好使用 Azure Managed Identity。这消除了对机密的需求并简化了对 AzureServiceTokenProvider 的构造函数调用(无参数 = 使用环境上下文 = 获取托管标识)。不幸的是,通过 Azure 进行本地开发并不那么容易。
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace N
{
public class Test
{
public async Task Main(IConfigurationRoot config, ILoggerFactory loggerFactory)
{
using var httpClient = new HttpClient(new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) =>
{
return true;
}
});
var url = "https://localhost:5000";
var clientId = "guid of your caller's identity App Registration";
var clientSecret = "secret you produced for your caller's managed identity App Registration";
var tenantId = "guid (Azure tenant id)";
var runAs = $"RunAs=App;AppId={clientId};TenantId={tenantId};AppKey={clientSecret}";
var tokenProvider = new AzureServiceTokenProvider(runAs);
var accessToken = await tokenProvider.GetAccessTokenAsync("https://my-test-app-identity.azurewebsites.net");
var requestMessage = new HttpRequestMessage(HttpMethod.Get, new Uri(uri));
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = await httpClient.SendAsync(requestMessage);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
foreach (var header in response.Headers)
{
Console.WriteLine(header.Key + ": " + string.Join(",", header.Value));
}
Console.WriteLine(content);
}
}
}
.csproj 中的依赖项:
<PackageReference Include="Azure.Core" Version="1.4.1" />
<PackageReference Include="Azure.Identity" Version="1.2.2" />
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.1.0" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.1.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.17.1" />
我正在尝试使用 Azure Active Directory 身份验证创建身份验证,我的应用程序由 WebApis 和 WEb 页面组成,我想要实现的是在用户请求应用程序时将用户重定向到 Microsoft 登录页面“控制器”页面,并在通过令牌调用 API 控制器时授权客户端应用程序。
我通过如下设置 Startup.Auth.cs 来做到这一点
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.IdToken,
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
}
});
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters { SaveSigninToken = true, ValidAudience = $"api://{clientId}" }
});
}
用 [Authorize] 属性装饰我的控制器
但是,当未经授权的客户端应用程序调用 API 时,它会返回以下
<html>
<head>
<title>Working...</title>
</head>
<body>
<form method="POST" name="hiddenform" action="https://local:44341/">
<input type="hidden" name="id_token" value="eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Im5PbzNaRHJPRFhFSzFqS1doWHNsSFJfS1hFZyJ9.eyJhdWQiOiI3NjIzMjcyMC1hNmViLTRkOTctOTI3Yy1lMWFjNzRiODIyMjAiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vYjFmZTVlMmEtZjc1Yy00NDQxLTlmYjItNWFjYzAxYmEwMGQwL3YyLjAiLCJpYXQiOjE2MTA3MzAJub25jZSI6IjYzNzQ2MzI3OTEwNDAxMzk3Ni5aR0k1TmpCaU1XWXRPRGsxT0MwMFpEWmlMV0kxWXprdE9ESTFZMlZrTWpjeU0yWTJNRFZqTWpnNU9EY3RNak0yWkMwME1qWmpMV0UwTTJZdE5XSXpNVFZsTlRKa1pHRXgiLCJvaWQiOiJiN2YzMjg1Mi1hMDRhLTRlOWUtYjFjOS02NTI5NDk3YTgyZDEiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiIyMjQxN0Byai5jb20iLCJyaCI6IjAuQUFBQUtsNy1zVnozUVVWoeHm6x2ad02ExHlw48lfRVZhEoHw8cZ5SWRFOGZfi3TJ3aOFKdaDZZ8Yt1no30Hku8pL3Vf7Lrq-BuKX8PNtkU45aVO1fZf8PFdM4UbpYnHOwvuG1NlPG8mTa1KheQTza9j3PTaiLq8e15jtSoFoHfIJbMZJoNTfvAF40kt9XvYee-rga80Oj1tJX78g_80MmRYORwArr1rq1n6EyPgFHaDDF5vD-zWOLDrXKyj2rwW-7LLpbtojtbsyCPdM5QPkLKnZZFanpvwRAFRTHaLdHINGAlHhvhFP9kvRhVtaTUgwYzrFqlN8k3zSZrvnMOec7A" /><input type="hidden" name="state" value="OpenIdConnect.AuthenticationProperties=7_QsmCbQZ3vBxo1tTvZXcYRLeNQMBZfanq5zpZvqNjuudSAu52-UZnVGkkWMXeBh_rIHE3i_j8g_B751WFqQHR1CXJrdjBi6PZy-" /><input type="hidden" name="session_state" value="5938bc609" /><noscript>
<p>Script is disabled. Click Submit to continue.</p><input type="submit" value="Submit" /></noscript></form>
<script language="javascript">
document.forms[0].submit();
</script>
</body>
</html>
但是当从我的函数中删除对 UseOpenIdConnectAuthentication 的调用时,它返回正确的响应消息,状态代码为 401
{ "Message": "Authorization has been denied for this request."}
话虽如此,谁能帮助我了解如何为 Web api 设置不记名令牌身份验证,以及如何为 Web 应用程序页面设置 openIdConnect。
谢谢,如果我的方法不对,请告诉我,并根据我的要求推荐正确的方法。
不要在此处使用 UseOpenIdConnectAuthentication,它用于自定义 OpenID 支持,这在此处并不适用,具有开箱即用的 Azure AD 特定帮助程序,例如您接下来使用的帮助程序。
这是服务器端的一个很好的起点:https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-overview
对于客户端,通用调用者(控制台应用程序或任意服务):https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-daemon-overview
对于客户端,需要自我保护的交互式网络应用程序:https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-web-app-sign-user-overview
简而言之,您的 API 客户端必须向 Azure AD 请求令牌才能访问您的 API 端点。要让 Azure AD 知道此令牌用于哪个资源,请为您的 API 创建应用程序注册,并使用其客户端 ID 和任何其他必要参数配置 API 应用程序。
当您的应用程序收到令牌时,它必须联系 Azure AD 来验证它。对于此调用,它应该使用您在 Azure AD 中为其生成的客户端 ID。
您可能希望在模拟真实的 AD 登录时 运行 客户端,或者您可能 运行 客户端作为某个 AD 主体(应用程序注册)。查看下面的 .NET Core 3.1 客户端应用程序示例是否可以帮助您。它使用其 ID 和密码模拟一些 AD 主体,因此它可以 运行 并从 Azure 云外部连接到受 Azure AD 保护的服务,而无需托管身份。
为了使下面的示例正常工作,请确保正确配置客户端应用程序和服务器 API 的主体(应用程序注册/企业应用程序)。这本身就是一个话题,在上面链接的官方文档中有详细描述。
如果您的客户端 运行 作为 Azure AppService 或在 Azure VM 上,最好使用 Azure Managed Identity。这消除了对机密的需求并简化了对 AzureServiceTokenProvider 的构造函数调用(无参数 = 使用环境上下文 = 获取托管标识)。不幸的是,通过 Azure 进行本地开发并不那么容易。
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace N
{
public class Test
{
public async Task Main(IConfigurationRoot config, ILoggerFactory loggerFactory)
{
using var httpClient = new HttpClient(new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) =>
{
return true;
}
});
var url = "https://localhost:5000";
var clientId = "guid of your caller's identity App Registration";
var clientSecret = "secret you produced for your caller's managed identity App Registration";
var tenantId = "guid (Azure tenant id)";
var runAs = $"RunAs=App;AppId={clientId};TenantId={tenantId};AppKey={clientSecret}";
var tokenProvider = new AzureServiceTokenProvider(runAs);
var accessToken = await tokenProvider.GetAccessTokenAsync("https://my-test-app-identity.azurewebsites.net");
var requestMessage = new HttpRequestMessage(HttpMethod.Get, new Uri(uri));
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = await httpClient.SendAsync(requestMessage);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
foreach (var header in response.Headers)
{
Console.WriteLine(header.Key + ": " + string.Join(",", header.Value));
}
Console.WriteLine(content);
}
}
}
.csproj 中的依赖项:
<PackageReference Include="Azure.Core" Version="1.4.1" />
<PackageReference Include="Azure.Identity" Version="1.2.2" />
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.1.0" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.1.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.17.1" />