在 ASP.NET Core 中,这些设置位于 appsettings.json 文件的“Microsoft Entra ID”节中。
{
"AzureAd": {
"Instance": "https://login.partner.microsoftonline.cn/",
"TenantId": "[Enter the tenantId here]",
// Client ID (application ID) obtained from the Azure portal
"ClientId": "[Enter the Client Id here]",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath": "/signout-oidc"
}
}
在 ASP.NET Core 中,另一个文件 (properties\launchSettings.json) 包含应用程序的 URL (applicationUrl
) 和 TLS/SSL 端口 (sslPort
),以及各种配置文件。
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:3110/",
"sslPort": 44321
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"webApp": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:3110/"
}
}
}
在 Azure 门户中,在应用程序的“身份验证”页中注册的重定向 URI 需与这些 URL 相匹配。 对于上述两个配置文件,URL 为 https://localhost:44321/signin-oidc
。 原因是 applicationUrl
为 http://localhost:3110
,但指定的是 sslPort
(44321
)。
CallbackPath
是 /signin-oidc
中定义的 appsettings.json
。
注销 URI 将采用相同方式设置为 https://localhost:44321/signout-oidc
。
备注
SignedOutCallbackPath 应设置为门户或应用程序,以避免在处理事件时发生冲突。
在 ASP.NET 中,应用程序通过 appsettings.json 文件的第 12 到 15 行进行配置。
{
"AzureAd": {
"Instance": "https://login.partner.microsoftonline.cn/",
"TenantId": "[Enter the tenantId here]",
"ClientId": "[Enter the Client Id here]",
}
}
在 Azure 门户中,在应用程序的“身份验证”页中注册的回复 URI 需与这些 URL 相匹配。 即,它们应是 https://localhost:44326/
。
在 Java 中,配置位于 application.properties 文件中的 src/main/resources
下。
aad.clientId=Enter_the_Application_Id_here
aad.authority=https://login.partner.microsoftonline.cn/Enter_the_Tenant_Info_Here/
aad.secretKey=Enter_the_Client_Secret_Here
aad.redirectUriSignin=http://localhost:8080/msal4jsample/secure/aad
aad.redirectUriGraph=http://localhost:8080/msal4jsample/graph/me
在 Azure 门户中,在应用程序的“身份验证”页中注册的回复 URI 需与应用程序定义的 实例相匹配。 即,它们应是 http://localhost:8080/msal4jsample/secure/aad
和 http://localhost:8080/msal4jsample/graph/me
。
此处,配置参数以环境变量的形式驻留在 .env.dev 中:
CLOUD_INSTANCE="Enter_the_Cloud_Instance_Id_Here" # cloud instance string should end with a trailing slash
TENANT_ID="Enter_the_Tenant_Info_Here"
CLIENT_ID="Enter_the_Application_Id_Here"
CLIENT_SECRET="Enter_the_Client_Secret_Here"
REDIRECT_URI="http://localhost:3000/auth/redirect"
POST_LOGOUT_REDIRECT_URI="http://localhost:3000"
GRAPH_API_ENDPOINT="Enter_the_Graph_Endpoint_Here" # graph api endpoint string should end with a trailing slash
EXPRESS_SESSION_SECRET="Enter_the_Express_Session_Secret_Here"
这些参数用于在 authConfig.js 文件中创建配置对象,最终将用于初始化 MSAL 节点:
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
require('dotenv').config({ path: '.env.dev' });
/**
* Configuration object to be passed to MSAL instance on creation.
* For a full list of MSAL Node configuration parameters, visit:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/configuration.md
*/
const msalConfig = {
auth: {
clientId: process.env.CLIENT_ID, // 'Application (client) ID' of app registration in Azure portal - this value is a GUID
authority: process.env.CLOUD_INSTANCE + process.env.TENANT_ID, // Full directory URL, in the form of https://login.partner.microsoftonline.cn/<tenant>
clientSecret: process.env.CLIENT_SECRET // Client secret generated from the app registration in Azure portal
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: 3,
}
}
}
const REDIRECT_URI = process.env.REDIRECT_URI;
const POST_LOGOUT_REDIRECT_URI = process.env.POST_LOGOUT_REDIRECT_URI;
const GRAPH_ME_ENDPOINT = process.env.GRAPH_API_ENDPOINT + "v1.0/me";
module.exports = {
msalConfig,
REDIRECT_URI,
POST_LOGOUT_REDIRECT_URI,
GRAPH_ME_ENDPOINT
};
在 Azure 门户中,在应用程序的“身份验证”页中注册的回复 URI 需与应用程序定义的 redirectUri 实例 (http://localhost:3000/auth/redirect
) 相匹配。
在本文中,为简单起见,客户端机密存储在配置文件中。 在生产应用中,请考虑使用密钥保管库或环境变量。 更好的选择是使用证书。
配置参数在 .env 中设置为环境变量:
# Note: If you are using Azure App Service, go to your app's Configuration,
# and then set the following values into your app's "Application settings".
CLIENT_ID=<client id>
CLIENT_SECRET=<client secret>
# Expects a full tenant id such as "contoso.partner.onmschina.cn", or its GUID
# Or leave it undefined if you are building a multi-tenant app
#TENANT_ID=<tenant id>
app_config.py 中引用了这些环境变量:
import os
# Application (client) ID of app registration
CLIENT_ID = os.getenv("CLIENT_ID")
# Application's generated client secret: never check this into source control!
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
# AUTHORITY = "https://login.partner.microsoftonline.cn/common" # For multi-tenant app
AUTHORITY = f"https://login.partner.microsoftonline.cn/{os.getenv('TENANT_ID', 'common')}"
REDIRECT_PATH = "/getAToken" # Used for forming an absolute URL to your redirect URI.
# The absolute URL must match the redirect URI you set
# in the app's registration in the Azure portal.
ENDPOINT = 'https://microsoftgraph.chinacloudapi.cn/v1.0/users' # This resource requires no admin consent
# You can find the proper permission names from this document
# https://docs.microsoft.com/en-us/graph/permissions-reference
SCOPE = ["User.ReadBasic.All"]
# Tells the Flask-session extension to store sessions in the filesystem
SESSION_TYPE = "filesystem"
# Using the file system will not work in most production systems,
# it's better to use a database-backed session store instead.
永远不要将 .env 文件签入源代码管理,因为它包含机密。 快速入门示例包含阻止签入 .env 文件的 .gitignore 文件。
# Environments
.env
初始化代码的差异与平台相关。 对于 ASP.NET Core 和 ASP.NET,用户登录将委托给 OpenID Connect 中间件来完成。 ASP.NET 或 ASP.NET Core 模板会为 Azure AD v1.0 终结点生成 Web 应用程序。 只需进行一些配置就能使这些应用程序适应 Microsoft 标识平台。
在 ASP.NET Core Web 应用(和 Web API)中,应用程序受到保护,因为控制器或控制器操作中包含 Authorize
属性。 此属性检查是否已对用户进行身份验证。 在 .NET 6 发布之前,代码初始化位于 Startup.cs 文件中。 使用 .NET 6 的新 ASP.NET Core 项目不再包含 Startup.cs 文件。 代替它的是 Program.cs 文件。 本教程的其余部分与 .NET 5 或更低版本相关。
备注
如果你想直接从 Microsoft 标识平台的利用 Microsoft.Identity.Web 的新 ASP.NET Core 模板开始,可以下载一个包含 .NET 5.0 项目模板的 NuGet 预览包。 然后,在安装后,可以直接实例化 ASP.NET Core Web 应用程序(MVC 或 Blazor)。 有关详细信息,请参阅 Microsoft.Identity.Web Web 应用项目模板。 这是最简单的方法,因为它将执行以下所有步骤。
如果你想使用 Visual Studio 中的当前默认 ASP.NET Core Web 项目来启动项目,或者使用 dotnet new mvc --auth SingleOrg
或 dotnet new webapp --auth SingleOrg
来启动项目,则会看到如下所示的代码:
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
此代码使用旧版 Microsoft.AspNetCore.Authentication.AzureAD.UI NuGet 包,该包用于创建 Azure Active Directory v1.0 应用程序。 本文介绍如何创建一个用于替换该代码的 Microsoft 标识平台 v2.0 应用程序。
将 Microsoft.Identity.Web 和 Microsoft.Identity.Web.UI NuGet 包添加到项目。 删除 Microsoft.AspNetCore.Authentication.AzureAD.UI
NuGet 包(如果存在)。
更新 ConfigureServices
中的代码,使其使用 AddMicrosoftIdentityWebApp
和 AddMicrosoftIdentityUI
方法。
public class Startup
{
...
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration, "AzureAd");
services.AddRazorPages().AddMvcOptions(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();
在 Startup.cs 的 Configure
方法中,通过调用 和 app.UseAuthentication();
启用身份验证。
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// more code here
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.MapControllers();
// more code here
}
在该代码中:
AddMicrosoftIdentityWebApp
扩展方法在 Microsoft.Identity.Web 中定义,其中:
- 配置用于读取配置文件的选项(此处来自“Microsoft Entra ID”节)
- 配置 OpenID Connect 选项,将颁发机构设为 Microsoft 标识平台。
- 验证令牌的颁发者。
- 确保从 ID 令牌中的
preferred_username
声明映射与名称对应的声明。
除了配置对象以外,还可以在调用 AddMicrosoftIdentityWebApp
时指定配置节的名称。 该名称默认为 AzureAd
。
AddMicrosoftIdentityWebApp
包含高级方案的其他参数。 例如,当身份验证不起作用时,跟踪 OpenID Connect 中间件事件有助于排查 Web 应用程序的问题。 将可选参数 subscribeToOpenIdConnectMiddlewareDiagnosticsEvents
设为 true
会显示 ASP.NET Core 中间件集在从 HTTP 响应进展到 HttpContext.User
中用户标识的过程中是如何处理信息的。
AddMicrosoftIdentityUI
扩展方法在 Microsoft.Identity.Web.UI 中进行定义。 它提供了一个默认控制器来处理登录和注销。
若要详细了解如何使用 Microsoft.Identity.Web 创建 Web 应用,请参阅 microsoft-identity-web 中的 Web 应用。
与 ASP.NET Web 应用和 Web API 中的身份验证相关的代码位于 App_Start/Startup.Auth.cs 文件中。
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
OwinTokenAcquirerFactory factory = TokenAcquirerFactory.GetDefaultInstance<OwinTokenAcquirerFactory>();
factory.Build();
app.AddMicrosoftIdentityWebApi(factory);
}
该 Java 示例使用 Spring 框架。 应用程序受到保护,因为已实现一个用于截获每个 HTTP 响应的筛选器。 在 Java Web 应用快速入门中,此筛选器是 AuthFilter
中的 src/main/java/com/microsoft/azure/msalwebsample/AuthFilter.java
。
该筛选器处理 OAuth 2.0 授权代码流,并检查用户是否已完成身份验证(isAuthenticated()
方法)。 如果用户未完成身份验证,它会计算 Microsoft Entra 授权终结点的 URL,并将浏览器重定向到此 URI。
包含授权代码的响应抵达时,它将使用 MSAL Java 获取令牌。 当它最终从令牌终结点收到令牌时(在重定向 URI 上),用户已登录。
有关详细信息,请参阅 doFilter()
中的 方法。
备注
doFilter()
的代码以略微不同的顺序编写,但流与上述相同。
有关此方法触发的授权代码流的详细信息,请参阅 Microsoft 标识平台和 OAuth 2.0 授权代码流。
节点示例使用 Express 框架。 MSAL 在 auth 路由处理程序中初始化:
var express = require('express');
const authProvider = require('../auth/AuthProvider');
const { REDIRECT_URI, POST_LOGOUT_REDIRECT_URI } = require('../authConfig');
const router = express.Router();
router.get('/signin', authProvider.login({
scopes: [],
redirectUri: REDIRECT_URI,
successRedirect: '/'
Python 示例是使用 Flask 框架生成的,但也可以使用 Django 等其他框架。 Flask 应用使用 app.py 顶部的应用配置进行初始化:
import identity
import identity.web
import requests
from flask import Flask, redirect, render_template, request, session, url_for
from flask_session import Session
import app_config
app = Flask(__name__)
app.config.from_object(app_config)
Session(app)
然后,代码使用auth
构造 对象。
auth = identity.web.Auth(
session=session,
authority=app.config.get("AUTHORITY"),
client_id=app.config["CLIENT_ID"],
client_credential=app.config["CLIENT_SECRET"],
)