AAD B2C服务有三个关键组成部分:

  • 用户目录。这个目录存储关于本地用户和联邦用户的信息,可以用Microsoft Graph访问。
  • 安全令牌服务(STS),其工作是发布令牌和其他身份验证货币,并验证令牌的真实性。
  • 一个被称为身份体验框架(IEF)的框架,作为一个运行时平台,用于协调与AAD B2C服务实例公开的身份验证端点之间的交互。

在AAD B2C中,用户交互是由策略定义的(也称为用户行程或用户流)。IEF托管的策略(内置的和定制的)是使用基于xml的伪编程语法定义的。

创建AAD B2C租户

  • 首先您需要做的是创建一个Azure AD B2C租户,并使用这些说明将其链接到您的Azure订阅。一定要记住你给B2C租户的名字。
  • 在Azure Portal中打开AAD B2C租户设置页面,并在左侧的AAD B2C菜单中单击身份体验框架。

构建初始策略

让我们构建并上传最简单的自定义策略。使用你喜欢的XML编辑器,创建一个新的XML文件包含以下内容,用这个名字你给AAD B2C租户两YOUR_TENANT_NAME条目(TenantId和PublicPolicyUri):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TrustFrameworkPolicyxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06"PolicySchemaVersion="0.3.0.0"TenantId="YOUR_TENANT_NAME.onmicrosoft.com"PolicyId="B2C_1A_Example"PublicPolicyUri="http://YOUR_TENANT_NAME.onmicrosoft.com/B2C_1A_Example">
</TrustFrameworkPolicy>

根TrustFrameworkPolicy元素显示一些关于您所使用的XML元信息,策略文件的模式版本(0.3.0.0)的ID AAD B2C租户,政策文件将驻留在(YOUR_TENANT_NAME.onmicrosoft.com),这个ID将被用来引用您的政策(B2C_1A_Example),以及在部署该策略时将用于引用该策略的URI (http://YOUR_TENANT_NAME.onmicrosoft.com/B2C_1A_Example)——它是TenantId和PolicyId的连接。

上传初始策略文件

通过单击上传自定义策略按钮,将文件上载到您的AAD B2C租户。选择策略XML文件并单击上传。

注意,上传策略会导致一个错误:

Validation failed: 1 validation error(s) found in policy “B2C_1A_EXAMPLE” of tenant “custompolicies.onmicrosoft.com”. A policy must have a technical profile “TpEngine_c3bd4fe2-1775-4013-b91d-35f16d377d13” with a metadata setting “url” that specifies the URL of the TPEngine service. Policy “B2C_1A_Example” in tenant “custompolicies.onmicrosoft.com” either does not contain such a technical profile or the technical profile does not contain the setting.

当您上传您的策略文件时,AAD B2C将尝试尽可能地解析和验证它们的内容。如果策略中的XML可以被认为是一种编程语言,那么上传文件就可以被认为与编译程序类似。

修正策略文件

通过在前面定义的TrustFrameworkPolicy元素中包含以下元素来更新策略文件。

<ClaimsProviders><ClaimsProvider><!-- The technical profile(s) defined in this section is required by the framework to be included in all policies. --><DisplayName>Trustframework Policy Engine TechnicalProfiles</DisplayName><TechnicalProfiles><TechnicalProfile Id="TpEngine_c3bd4fe2-1775-4013-b91d-35f16d377d13"><DisplayName>Trustframework Policy Engine Default Technical Profile</DisplayName><Protocol Name="None" /><Metadata><Item Key="url">{service:te}</Item></Metadata></TechnicalProfile></TechnicalProfiles></ClaimsProvider>
</ClaimsProviders>

重新上传修正后的策略文件

上传更新后的策略文件。当上传成功完成时,策略ID将在Identity Experience Framework面板中的自定义策略部分中列出。

到目前为止,我们已经讨论了一些关于AAD B2C中自定义策略的背景知识,并介绍了将自定义策略XML语法作为身份体验框架提供的运行时编程语言的想法。然后,我们准备并上传能够被Identity Experience Framework成功解析的最小的定制策略。然而,这个策略并不是非常有用——它基本上是一个空程序。我们需要做更多的工作。

声明Claims

从本系列前一篇文章中的策略文件开始。或者,您可以在这里下载该文件,确保从您的AAD B2C实例将TenantId、PolicyId和PublicPolicyUri属性更新为正确的值。将以下BuildingBlocks部分作为TrustFrameworkPolicy元素的第一个子元素添加到策略文件中,就在ClaimsProviders元素之前:

<BuildingBlocks><ClaimsSchema><ClaimType Id="objectId"><DataType>string</DataType></ClaimType><ClaimType Id="message"><DataType>string</DataType></ClaimType></ClaimsSchema>
</BuildingBlocks>

新添加的BuildingBlocks元素包含一个ClaimsSchema元素。在Claims Schema部分,您可以定义将在策略“程序”中使用的“变量”。在本例中,使用ClaimType元素声明了两个变量。
第一个声明是针对objectId声明/变量的。OpenID Connect策略需要包含一个声明,该声明唯一地标识从策略调用返回的声明的主题,其中OpenID Connect操作的主题基本上是最终用户。我们稍后将了解到Azure Active Directory使用声明objectId来标识用户和它存储的其他工件,所以我们在这里使用相同的ID名称,以便以后简化工作。
当您声明Claims时,除了ID外,还必须指出该声明的数据类型。在本例中,objectId声明将其DataType设置为字符串,因为它将用于保存文本值。
第二个声明非常相似。我们使用message和DateType为字符串的Claim来声明变量。这是将用于返回“Hello World”消息的声明。

创建令牌

OpenID Connect策略提供的任何声明都必须包含在该策略生成并返回给策略调用者的令牌中。为此,我们需要将策略配置为使用JWT令牌颁发者。

配置AAD B2C的签名和加密密钥

JWT令牌颁发者需要访问两个加密密钥,它们必须与AAD B2C实例中的策略一起存储。第一个密钥用于对策略运行时发出的JWT令牌进行数字签名。第二个密钥用于加密在策略运行时发出的任何刷新令牌。
在Azure Portal中打开您的AAD B2C租户设置页面,并在左侧的AAD B2C菜单中单击身份体验框架。单击策略键,然后单击+添加。

在创建密钥对话框中,设置下面的值,然后单击创建:

  • Set Options to Generate
  • Set Name to TokenSigningKeyContainer
  • Set Key type to RSA
  • Set Key usage to Signature

再次点击+添加,在创建一个密钥对话框中,设置下面的值,然后点击创建:

  • Set Options to Generate
  • Set Name to TokenEncryptionKeyContainer
  • Set Key type to RSA
  • Set Key usage to Encryption

注意,当您创建Cryptographic Key条目时,前缀B2C_1A_将自动被添加到密钥名称前面,就像您上传策略ID时的前缀一样。

声明JWT令牌颁发者

将以下ClaimsProvider元素添加到策略已经包含的ClaimsProviders元素中。如果您对在上一步中创建的Cryptographic Keys使用了不同的名称,请确保更新StorageReferenceId值以匹配您所使用的名称。

<ClaimsProvider><!--The technical profile(s) defined in this section specify Token Issuers that are used by the required SendClaims step of a User Journeyto return a token to the caller.--><DisplayName>Token Issuer Technical Profiles</DisplayName><TechnicalProfiles><TechnicalProfile Id="JwtIssuer"><DisplayName>JWT Issuer</DisplayName><Protocol Name="None" /><OutputTokenFormat>JWT</OutputTokenFormat><Metadata><Item Key="client_id">{service:te}</Item><Item Key="issuer_refresh_token_user_identity_claim_type">objectId</Item><Item Key="SendTokenResponseBodyWithJsonNumbers">true</Item></Metadata><CryptographicKeys><Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" /><Key Id="issuer_refresh_token_key" StorageReferenceId="B2C_1A_TokenEncryptionKeyContainer" /></CryptographicKeys><InputClaims /><OutputClaims /></TechnicalProfile></TechnicalProfiles>
</ClaimsProvider>

使用令牌颁发者

仅仅声明JWT令牌颁发者并不会导致它被您的策略使用。你必须将其作为用户旅程的一部分。
在策略中已经包含的ClaimsProviders元素之后添加以下UserJourneys元素:

<UserJourneys><UserJourney Id="HelloWorldJourney"><OrchestrationStep Order="1" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" /></UserJourney>
</UserJourneys>

UserJourney 指定调用策略时要遵循的步骤。在本例中,为UserJourney提供了ID HelloWorldJourney,稍后将在策略中引用它。它还包括一个具有SendClaims类型的步骤和对您之前定义的JWT令牌颁发者(JwtIssuer) ID的引用。

定义策略的入口点

策略的依赖方部分有两个关键职责——它必须指定要执行哪个用户旅程,它必须指出在策略运行时返回的令牌中包含哪些声明。
将以下RelyingParty元素添加到策略中,在刚才添加的UserJourneys元素之后:

<RelyingParty><DefaultUserJourney ReferenceId="HelloWorldJourney"/><TechnicalProfile Id="HelloWorldPolicyProfile"><DisplayName>Hello World Policy Profile</DisplayName><Protocol Name="OpenIdConnect" /><OutputClaims><OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" DefaultValue="Hello World Object ID"/><OutputClaim ClaimTypeReferenceId="message" DefaultValue="Hello World!"/></OutputClaims><SubjectNamingInfo ClaimType="sub" /></TechnicalProfile>
</RelyingParty>

RelyingParty元素包括一个DefaultUserJourney元素,它引用您前面创建的HelloWorldJourney用户旅程。运行此策略时,依赖方将调用该用户旅程。
RelyingParty元素内部的TechnicalProfile元素有一些重要的声明。它指定使用此策略进行交换的OpenIdConnect协议。它还包括两个将添加到策略生成的JWT令牌的输出声明——我们的objectId和消息声明。这些声明的值是用DefaultValue属性设置的,如果之前没有设置声明,则该属性将为声明提供一个值。
请注意,objectId声明包含一个额外的属性。PartnerClaimType属性用于为Claim提供一个不同于策略内部使用的外部名称。在本例中,将在令牌中发出的声明名称是sub,而不是objectId。

请注意
如前所述,OpenID Connect需要识别颁发令牌的对象。子声明在OpenID Connect规范中定义为主体标识符声明所需的名称。因为在其他AAD B2C策略操作中使用名称objectId很有用,所以当它通过使用PartnerClaimType属性在令牌中发出时,只提供一个别名是很方便的。

最终您的策略文件应该和下面一样:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TrustFrameworkPolicyxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06"PolicySchemaVersion="0.3.0.0"TenantId="YOUR_TENANT_NAME.onmicrosoft.com"PolicyId="YOUR_POLICY_ID"PublicPolicyUri="http://YOUR_TENANT_NAME.onmicrosoft.com/YOUR_POLICY_ID"><BuildingBlocks><ClaimsSchema><ClaimType Id="objectId"><DataType>string</DataType></ClaimType><ClaimType Id="message"><DataType>string</DataType></ClaimType></ClaimsSchema></BuildingBlocks><ClaimsProviders><ClaimsProvider><!-- The technical profile(s) defined in this section is required by the framework to be included in all policies. --><DisplayName>Trustframework Policy Engine TechnicalProfiles</DisplayName><TechnicalProfiles><TechnicalProfile Id="TpEngine_c3bd4fe2-1775-4013-b91d-35f16d377d13"><DisplayName>Trustframework Policy Engine Default Technical Profile</DisplayName><Protocol Name="None" /><Metadata><Item Key="url">{service:te}</Item></Metadata></TechnicalProfile></TechnicalProfiles></ClaimsProvider><ClaimsProvider><!--The technical profile(s) defined in this section specify Token Issuers that are used by the required SendClaims step of a User Journeyto return a token to the caller.--><DisplayName>Token Issuer Technical Profiles</DisplayName><TechnicalProfiles><TechnicalProfile Id="JwtIssuer"><DisplayName>JWT Issuer</DisplayName><Protocol Name="None" /><OutputTokenFormat>JWT</OutputTokenFormat><Metadata><Item Key="client_id">{service:te}</Item><Item Key="issuer_refresh_token_user_identity_claim_type">objectId</Item><Item Key="SendTokenResponseBodyWithJsonNumbers">true</Item></Metadata><CryptographicKeys><Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" /><Key Id="issuer_refresh_token_key" StorageReferenceId="B2C_1A_TokenEncryptionKeyContainer" /></CryptographicKeys><InputClaims /><OutputClaims /></TechnicalProfile></TechnicalProfiles></ClaimsProvider></ClaimsProviders><UserJourneys><UserJourney Id="HelloWorldJourney"><OrchestrationSteps><OrchestrationStep Order="1" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" /></OrchestrationSteps></UserJourney></UserJourneys><RelyingParty><DefaultUserJourney ReferenceId="HelloWorldJourney"/><TechnicalProfile Id="HelloWorldPolicyProfile"><DisplayName>Hello World Policy Profile</DisplayName><Protocol Name="OpenIdConnect" /><OutputClaims><OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" DefaultValue="Hello World Object ID"/><OutputClaim ClaimTypeReferenceId="message" DefaultValue="Hello World!"/></OutputClaims><SubjectNamingInfo ClaimType="sub" /></TechnicalProfile></RelyingParty>
</TrustFrameworkPolicy>

在本示例中我们将使用https://jwt.ms站点作为页面跳转的重定向URL。

TrustFrameworkPolicy Schema

现在是时候更深入地研究AAD B2C策略的内容了。以下5个关键部分组成了AAD B2C政策。这些被作为子元素包含在根元素TrustFrameworkPolicy中:

  • BasePolicy 用于在策略文件中构建继承层次结构的元素。
  • BuildingBlocks 这部分可以被看作是策略的“规范”部分 — 它包括声明(在策略中充当“变量”)、数据转换实用程序、用户界面内容和控制,以及本地化配置。这里定义的元素稍后会在ClaimsProviders和RelyingParty部分中被策略的“功能”所利用。
  • ClaimsProviders 可以被认为是编程语言的可执行函数的项,以称为技术概要文件的单元表示。
  • UserJourneys 此部分定义调用策略时执行的步骤序列,以及调用或跳过每个步骤的条件。
  • RelyingParty 定义了策略公开的端点的行为。这个元素的主要目的是定义策略执行哪些UserJourney,返回哪些声明,尽管也可以定义其他设置。

这些部分包含在AAD B2C自定义策略中,作为根元素TrustFrameworkPolicy的子元素:

<TrustFrameworkPolicyxmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="https://www.w3.org/2001/XMLSchema"xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06"PolicySchemaVersion="0.3.0.0"TenantId="YOUR_TENANT_NAME.onmicrosoft.com"PolicyId="YOUR_POLICY_ID"PublicPolicyUri="http://YOUR_TENANT_NAME.onmicrosoft.com/YOUR_POLICY_ID"><BasePolicy><!-- Content --></BasePolicy><BuildingBlocks><!-- Content --></BuildingBlocks><ClaimsProviders><!-- Content --></ClaimsProviders><UserJourneys><!-- Content --></UserJourneys><RelyingParty><!-- Content --></RelyingParty>
</TrustFrameworkPolicy>

这些元素的详细文档可以在网上找到。注意,BasePolicy元素的文档目前包含在TrustFrameworkPolicy元素的文档页面中。

建立策略文件的BuildingBlocks

“BuildingBlocks”部分可以被视为策略的“规范”部分,并且大部分包含了需要被策略后面的“函数”引用的核心声明。
BuildingBlocks元素可能包含以下子元素:

Element Purpose
ClaimsSchema 声明将被策略中的“函数”引用的任何声明。
ClaimsTransformations 指定策略“函数”使用的小操作,通过使用一组输入参数和/或其他声明的值执行指定的方法来设置一个声明的值。如果稍后讨论的Technical Profiles是策略的“功能”,那么这些可能是“操作”(这么说可能有点夸张)。
ContentDefinitions 定义HTML模板,其中包含将应用于用户界面的样式化和其他自定义。
Predicate and PredicateValidations 配置可附加到声明以提供数据验证的小操作。
Localization 指定特定于语言环境的语言元素。
DisplayControls 在完成显示用户界面控件的“功能”之前,配置能够与后端功能交互的用户界面控件。当前控件可触发操作,但通常在单击完成对话框的按钮时发生。

Claims在自定义策略中的作用

重要的是,首先要理解当Identity Experience Framework (IEF)运行时执行策略时,声明(变量)是如何使用的。当执行UserJourney部分中定义的步骤时,数据存储在内存中一个所谓的claim -bag中。操作通过从索赔包中检索索赔作为输入,并通过在索赔包中添加或更新索赔作为输出来释放索赔。整个过程以从索赔包中检索一组索赔结束,然后以令牌的形式从策略中返回这些索赔。这个序列如下图所示:

声明Claim

在 UserJourney中使用策略中的声明之前,必须首先声明它。声明是用构建块的ClaimsSchema部分中的ClaimType元素声明的。
在策略文件的ClaimsSchema部分中添加以下Claim Type声明,紧接在消息Claim的声明之后:

<ClaimType Id="givenName"><DisplayName>First Name</DisplayName><DataType>string</DataType><DefaultPartnerClaimTypes><Protocol Name="OAuth2" PartnerClaimType="given_name" /><Protocol Name="OpenIdConnect" PartnerClaimType="given_name" /><Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" /></DefaultPartnerClaimTypes><UserHelpText>Your given name (also known as first name).</UserHelpText><UserInputType>TextBox</UserInputType>
</ClaimType>
<ClaimType Id="surname"><DisplayName>Last Name</DisplayName><DataType>string</DataType><DefaultPartnerClaimTypes><Protocol Name="OAuth2" PartnerClaimType="family_name" /><Protocol Name="OpenIdConnect" PartnerClaimType="family_name" /><Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" /></DefaultPartnerClaimTypes><UserHelpText>Your surname (also known as family name or last name).</UserHelpText><UserInputType>TextBox</UserInputType>
</ClaimType>
<ClaimType Id="displayName"><DataType>string</DataType><DefaultPartnerClaimTypes><Protocol Name="OAuth2" PartnerClaimType="unique_name" /><Protocol Name="OpenIdConnect" PartnerClaimType="name" /><Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" /></DefaultPartnerClaimTypes>
</ClaimType>

这个标记声明了3个额外的Claim type——givenName、surName和displayName——每个声明包含的信息都比原始的objectId或message声明的信息多。

到目前为止,所有声明的数据类型值都是字符串。还有其他几种数据类型可以用来声明自定义策略声明:

DataType Content
string 文本值不超过256个字符
boolean True/False
int 32位有符号整型
long 64位有符号整型
date 日历日期值,格式为yyyy-MM-dd
dateTime 表示具体时间,格式为yyyy-MM-dd HH:mm:ssZ
phoneNumber 表示用于多因素身份验证集成的电话号码
stringCollection 一组文本值,用于同时保存多个值的变量
duration 表示以年、月、日、小时、分钟和秒为单位的时间间隔。格式为PnYnMnDTnHnMnS,其中P表示正数,N表示负数。nY表示年,后面跟着y, nMo表示月,后面跟着mo, nD表示天,后面跟着d,例如:P21Y表示21年。P1Y2Mo表示一年,两个月。P1Y2Mo5D代表1年、2个月、5天。P1Y2M5DT8H5M620S代表1年、2个月、5天、8小时、5分钟、20秒。
userIdentity 表示用户标识。
userIdentityCollection 表示userIdentity的集合。

您可能希望为任何数据大小或内容限制提供帮助文本。用户输入类型规定了将在用户界面中显示的实际控件类型。下表显示了当前可用的用户输入类型列表:

User Input Type Purpose
TextBox 单行文本框
EmailBox 基本的电子邮件输入框
Password 密码输入—在用户输入文本时屏蔽文本
DateTimeDropdown 提供日/月/年 下拉选择框
RadioSingleSelect 显示Claim的限制部分中包含的每个Enumeration元素的单选按钮
DropDownSingleSelect 提供一个下拉列表,允许从声明的“限制”部分中包含的枚举元素集中选择单个元素
CheckBoxMultiSelect 显示Claim的限制部分中包含的每个Enumeration元素的复选框集合,允许多次选择。选择在字符串声明中作为逗号分隔的列表返回。
Readonly 以只读模式显示包含声明值的文本框
Paragraph 将声明值显示为HTML p标签块中的只读文本

Claims Transformations

Claims transformation是策略“函数”使用的一些小操作,通过对给定的输入参数和/或输入索赔执行操作来设置索赔的值。每个转换通过TransformationMethod属性指定需要执行的操作。
在ClaimTypes元素的结束标记之后,将下面的Claims transformation元素声明添加到策略文件的BuildingBlocks部分。

<ClaimsTransformations><ClaimsTransformation Id="GenerateObjectIdTransformation" TransformationMethod="CreateRandomString"><InputParameters><InputParameter Id="randomGeneratorType" DataType="string" Value="GUID"/></InputParameters><OutputClaims><OutputClaim ClaimTypeReferenceId="objectId" TransformationClaimType="outputClaim"/></OutputClaims></ClaimsTransformation><ClaimsTransformation Id="CreateDisplayNameTransformation" TransformationMethod="FormatStringMultipleClaims"><InputClaims><InputClaim ClaimTypeReferenceId="givenName" TransformationClaimType="inputClaim1"/><InputClaim ClaimTypeReferenceId="surname" TransformationClaimType="inputClaim2"/></InputClaims><InputParameters><InputParameter Id="stringFormat" DataType="string" Value="{0} {1}"/></InputParameters><OutputClaims><OutputClaim ClaimTypeReferenceId="displayName" TransformationClaimType="outputClaim"/></OutputClaims></ClaimsTransformation><ClaimsTransformation Id="CreateMessageTransformation" TransformationMethod="FormatStringClaim"><InputClaims><InputClaim ClaimTypeReferenceId="displayName" TransformationClaimType="inputClaim"/></InputClaims><InputParameters><InputParameter Id="stringFormat" DataType="string" Value="Hello {0}"/></InputParameters><OutputClaims><OutputClaim ClaimTypeReferenceId="message" TransformationClaimType="outputClaim"/></OutputClaims></ClaimsTransformation>
</ClaimsTransformations>

这个标记声明了三个声明转换——GenerateObjectIdTransformation、CreateDisplayNameTransformation和CreateMessageTransformation。GenerateObjectIdTransformation指定在其TransformationMethod属性转换使用CreateRandomString方法,用于更新一个字符串声明一个随机值,在这种情况下一个GUID——按照InputParameter randomGeneratorType属性条目的值所需要的这种方法。使用新值更新的声明在OutputClaims部分中指定。

  • CreateDisplayNameTransformation转换指示了formatstringmultipleclclaims的转换方法——它接受一个输入参数,该参数指定一个格式字符串,该格式字符串需要两个输入和两个输入声明,它们被替换到格式字符串中以产生指定的输出声明。
  • CreateMessageTransformation转换指示FormatStringClaim的转换方法——它接受一个输入参数,该参数指定一个格式字符串,该格式字符串需要1个输入和一个声明,该声明被替换到格式字符串中,以产生指定的输出声明。

Content Definitions

Content Definitions允许您指定HTML模板,这些模板管理将显示给策略用户的页面的总体布局。当您的策略运行时,Content Definition中指定的元素将与声明的特定用户界面元素合并,以实现最终的页面内容。
ContentDefinition是由ContentDefinition元素和Id属性指定的。在元素中,您需要指定:

Data URIs

在使用这个ContentDefinition的页面上显示的数据的性质是由ContentDefinition元素中的DataUri子元素指定的。Data URI指导AAD B2C运行时用于将页面模板和单个用户界面元素混合到最终HTML页面的内容,指导该过程的关键元素是页面标识符。目前AAD B2C支持七个命名页面标识符:

Page Identifier Purpose
idpselection 列出用户登录时可以选择的标识提供程序。(注意——这将被下面的provider选择标识符所取代。)
providerselection 列出用户登录时可以选择的标识提供程序。用于1.2或更新版本的页面布局(页面布局版本将在稍后讨论。)
unifiedssp 用于显示本地帐户登录页面,该页面为用户提供调用注册流程的能力。这个“SSP”版本包括“保持我签入”和“忘记你的密码”页面元素时显示。
unifiedssd 用于显示本地帐户登录页面,该页面为用户提供调用注册流程的能力。这个“SSD”版本省略了“保持我的登录”和“忘记你的密码”页面元素。
multifactor 用于显示预构建的多因素身份验证(MFA)页面。
globalexception 用于发生未处理错误时显示的页面。
selfasserted 用于不具有上述预定用途之一的任何页面的Data URI。这是您最常用于自己的页面定义的Data URI。

Load URIs

Load URI元素是指定要使用的HTML模板页面的地方。Azure AD B2C目前定义了3种内置的模板样式,你可以使用它们——经典、海洋蓝和石板灰。或者你也可以提供你自己的HTML(定制你的页面布局HTML是另一篇文章的主题)。你可以在Azure AD B2C文档中关于这些模板和定制HTML/CSS的信息。
为了配合Data URI, AAD B2C还包括几个默认的模板页面URI,这取决于您希望使用的样式,或者您可以定义自己的模板页面并使用您:

Page Identifier Pages
idpselection or providerselection Classic: ~/tenant/default/idpSelector.cshtml Ocean Blue: ~/tenant/templates/AzureBlue/idpSelector.cshtml Slate Gray: ~/tenant/templates/MSA/idpSelector.cshtml
unifiedssp or unifiedssd Classic: ~/tenant/default/unified.cshtml Ocean Blue: ~/tenant/templates/AzureBlue/unified.cshtml Slate Gray: ~/tenant/templates/MSA/unified.cshtml
multifactor Classic: ~/tenant/default/multifactor-1.0.0.cshtml Ocean Blue: ~/tenant/templates/AzureBlue/multifactor-1.0.0.cshtml Slate Gray: ~/tenant/templates/MSA/multifactor-1.0.0.cshtml
globalexception Classic: ~/tenant/default/exception.cshtml Ocean Blue: ~/tenant/templates/AzureBlue/exception.cshtml Slate Gray: ~/tenant/templates/MSA/exception.cshtml

您可以在https://docs.microsoft.com/en-us/samples/azure-samples/azure-ad-b2c-page-templates/azure-ad-b2c-page-templates找到Ocean Blue和Slate Gray模板定义所使用的HTML和CSS文件。

为我们的策略添加内容定义

将下面的Content Definitions元素声明添加到策略文件的BuildingBlocks部分,紧接在claimstranstions元素的结束标记之后。

<ContentDefinitions><ContentDefinition Id="SelfAssertedContentDefinition"><LoadUri>~/tenant/default/selfAsserted.cshtml</LoadUri><RecoveryUri>~/common/default_page_error.html</RecoveryUri><DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:1.2.0</DataUri></ContentDefinition>
</ContentDefinitions>

我们用SelfAssertedContentDefinition的ID来声明一个ContentDefinition。它使用内置的Classic自断言页面模板和自断言版本1.2 Data URI。还指定了RecoveryUri元素——这是一个必需的元素,应该保持原样。

Functions in the Program – Technical Profiles

现在我们已经声明了一些“变量”(声明)、声明转换和内容定义,接下来要做的事情是添加一些“函数”来实际处理这些工件。策略中的“函数”角色由TechnicalProfile元素处理,这些元素包含在Claims Providers中。

The ClaimsProviders and ClaimsProvider Elements

技术概要文件元素在TrustFrameworkPolicy的ClaimsProviders部分的ClaimsProvider元素中指定。正如名称Claims Provider所暗示的,大多数Technical Profiles接受输入声明和一些配置数据,并返回(或提供)一组输出声明。
将以下空的ClaimsProvider部分添加到策略的现有ClaimsProviders元素中:

<ClaimsProvider><!--The technical profiles defined in this section are used to generate values for claims thatare being collected as part of the user journey.--><DisplayName>Claim Generator Technical Profiles</DisplayName>
</ClaimsProvider>

ClaimsProviders元素可以包含几个ClaimsProvider元素,每个元素可以包含多个TechnicalProfile元素。这允许您通过将具有类似目的的功能分组到同一个Claims Provider中来组织事情。

技术配置文件元素类型

IEF定义了一组有限的技术概要文件类型,您可以在AAD B2C自定义策略中包含它们。换句话说,只有某些函数可以在策略中执行,尽管随着时间的推移会添加新的概要文件类型。在撰写本文时,这些问题包括:

Technical Profile Type Purpose
Claims Transformation Alters or validates the values of claims.
Self-Asserted Shows user interface elements to the end-user to display and/or collect claims values.
REST Makes a call to the REST endpoint defined for an external service in order to process and/or to retrieve some claims values.
Azure Active Directory Performs storage and retrieval operations against the underlying AAD user store.
OAuth1 OAuth2 OpenID Connect SAML2 Connects with identity providers that implement the indicated protocol.
Session Management Coordinates storing and retrieving claims across user sessions.
JWT Token Issuer Constructs a JWT token that is returned by an AAD B2C policy.
One-time Password Either generates a code that can be sent to a user outside of AAD B2C (for example, via email) for them to to then enter in a UI element, or validates that the code that a user has provided matches teh code that was generated for them.
Phone Factor Currently in Preview. Provides phone-based multi-factor authentication (SMS or Phone Call). Renders its own UI page.
Azure MFA Currently in Preview. Provides phone-based multi-factor authentication (SMS only). You supply the UI.
Azure Application Insights Currently in Preview. Supports logging usage information to Azure Application Insights.

请注意
由于其中一种概要文件类型允许您进行REST调用,如果您发现需要比现有的Technical profile类型提供的功能更多的功能,那么您可以实现并调用自己的API端点。

Technical Profile Execution

当AAD B2C IEF运行时执行一个Technical Profile时,它实际上流经一个指定的步骤序列。这个顺序如下图所示(注:此图来自AAD B2C自定义政策技术概要文档):

包括这些步骤:

  • 检索技术概要文件的会话状态声明(如果有的话),并将其存储在索赔包中。
  • 应用任何指定的输入声明转换。从索赔包中检索必要的索赔,应用转换,并将转换结果放回索赔包中。
  • 从索赔袋中提取技术简介所要求的任何其他索赔。
  • 实际执行Technical Profile的核心功能。
  • 应用任何已声明的验证技术概要文件(我们将在本系列的下一篇文章中讨论这些内容)。这些只能为自我断言技术概要文件声明。
  • 将任何已声明的输出索赔发送到索赔包。
  • 应用任何指定的Output Claims转换。与输入索赔转换一样,从索赔包中检索必要的索赔,应用转换,并将转换的结果放回索赔包中。
  • 从claims包中为技术概要编写任何已声明的会话状态声明。

现在我们已经从高层次上了解了Technical Profile的工作方式,接下来让我们看看如何利用其中的一些来创建个性化的问候。

Claims Transformation Technical Profiles

我们将首先关注一种技术概要文件——索赔转换技术概要文件类型,从而开始对TechnicalProfile元素的讨论。顾名思义,索赔转换技术概要文件通常用于应用在策略的BuildingBlocks部分中定义的索赔转换操作。索赔转换技术概要文件类型本身并没有上面所示的协议执行步骤。它仅仅运行序列的输入和输出声明转换部分,并执行协议执行步骤

Making the objectId Claim Value Random

将以下TechnicalProfiles内容添加到刚才添加到策略文件的ClaimsProvider元素中,否则该元素为空,紧跟在它所包含的DisplayName元素之后:

<TechnicalProfiles><TechnicalProfile Id="RandomObjectIdClaimGenerator"><!--In order for an Object ID to be returned (this is required in an interactive profile) one of the journey steps must include it as an output claim.In this case, it is being done as a claims transformation without any input, but instead is generating a random value using a claims transformation.--><DisplayName>Random Object ID Claim Generator Technical Profile</DisplayName><Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /><OutputClaims><OutputClaim ClaimTypeReferenceId="objectId"/></OutputClaims><OutputClaimsTransformations><OutputClaimsTransformation ReferenceId="GenerateObjectIdTransformation"/></OutputClaimsTransformations></TechnicalProfile>
</TechnicalProfiles>

这个技术概要文件有一个ID为RandomObjectIdClaimGenerator,它包括一个DisplayName元素,这是所有技术概要文件所需要的。技术概要文件的类型——以及它的功能——通过包含所需的Protocol元素来设置。对于Claims Transformation技术概要文件,您指定协议的Name是专有的,它的Handler属性设置为Web.TPEngine.Providers。ClaimsTransformationProtocolProvider,网络。TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null。要找到正确的Name和(如果需要的话)Handler值

请注意
您可能会认为Handler值是. net程序集的完全限定名。IEF使用其中几个程序集为Technical Profiles提供不同的功能。这些程序集由IEF运行时维护和管理,在撰写本文时还没有公开。

这个技术概要文件还包括一个OutputClaims部分,其中有一个对objectId声明的引用。索赔转换技术概要文件必须包括至少一个输出索赔。
randomobjecdclaimgenerator技术概要文件中的最后一个元素是outputclaimstranstions元素中的outputclaimtransform声明。这个Output Claims转换包含一个ReferenceId属性,它引用了在策略的“构建块”部分中声明的Claims转换的GenerateObjectIdTransformation ID。在本例中,是Claims转换

请注意
实际上,创建一个甚至不包含任何Claims Transformation的Claims Transformation Technical Profile是可能的(有时也是非常有用的),而只是包含一个或多个具有DefaultValue属性的OutputClaim元素,该属性用于设置索赔值。我们在“Hello World!”的RelyingParty元素的Technical Profile中看到了类似的情况。策略,其中message claim仅通过对Output claim应用默认值来设置。

Setting the displayName and message Claim Values

现在,在randomobjecdclaimgenerator技术概要文件中包含的同一个TechnicalProfiles元素中,向策略添加以下技术概要文件。

<TechnicalProfile Id="UserInputMessageClaimGenerator"><!--Generates the content of the message claim by applying the relevant claims transformation--><DisplayName>Display Name Claim Generator Technical Profile</DisplayName><Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /><OutputClaims><OutputClaim ClaimTypeReferenceId="displayName"/><OutputClaim ClaimTypeReferenceId="message"/></OutputClaims><OutputClaimsTransformations><OutputClaimsTransformation ReferenceId="CreateDisplayNameTransformation"/><OutputClaimsTransformation ReferenceId="CreateMessageTransformation"/></OutputClaimsTransformations>
</TechnicalProfile>

上面声明的技术概要文件有一个UserInputMessageClaimGenerator的ID,并且类似于它之前的randomobjecdclaimgenerator技术概要文件。在本例中,新的Technical Profile输出displayName和message声明,并调用一对claims transformation来填充这些声明。它首先调用CreateDisplayNameTransformation转换来设置displayName值,然后调用CreateMessageTransformation转换来使用该displayName值来设置消息Output Claim中的个性化欢迎语。

请注意
objectId和消息声明都可以作为包含两个OutputClaim条目的单个Technical Profile的结果输出,而不是使用两个Technical Profile来这样做。在本系列后面的文章中,用来填充这些声明的功能将采用不同的方式。他们现在是分开的,以便那时更容易处理。

Collecting User Input

上面使用的Claims Exchange Technical Profiles根据用户的输入设置我们的个性化问候消息,但我们还没有收集这些输入。因此,下一步是创建允许用户输入姓名的Technical Profile。
收集用户输入需要使用Self-Asserted Technical Profile。这些技术概要文件与Display Claims、Input Claims、Output Claims和Metadata元素一起工作,以呈现用户界面元素,并交互地从用户那里收集信息。

Displaying and Collecting Claims in the Technical Profile

DisplayClaims使用DisplayClaims元素指定,该元素包含一个DisplayClaim项的集合。每个显示索赔包含对策略的“构建块”部分中声明的索赔类型之一的引用,并指出索赔的用户界面类型中指定的用户界面元素应该显示在由技术概要文件显示的页面上。如果希望要求用户为Display Claim提供一个值,请将其Required属性设置为true。
Input Claims用于为显示的索赔提供初始值。如果索赔已经在clails - Bag中设置,提供一个引用索赔的Input claim条目将包括在显示用户界面元素时为其显示的控件中的索赔claim Bag值。如果索赔没有在claims包中设置,那么将其包含在InputClaims集合中并设置其DefaultValue属性也将为UI元素提供初始值。
最后,Output Claims用于确定从Technical Profile返回哪些值,并将其存储到策略执行的索赔包中。
自我断言的技术概要文件也支持包括inputclaimstranstions和outputclaimstranstions。转换将分别在从索赔包中检索输入索赔之前或在将输出索赔写入索赔包之后被调用。

请注意
显示声明是AAD B2C自定义策略中最近添加的一个内容,实际上它们目前处于预览状态。因此,有几件事需要记住。首先,XSD模式不知道它们,如果您使用模式验证工具编辑策略,它们将被标记。其次,在引入显示索赔之前,输出索赔承担了双重责任——它们指定了将在用户界面中显示的索赔,并指出了将存储在索赔包中的索赔。

Technical Profile Metadata

除了使用显示、输入和输出声明之外,Self-Asserted Technical Profiles还可以使用Technical Profile的Metadata元素中声明的元素。元数据元素是用来提供一个技术概要文件与配置文件中定义的静态值XML本身(或semi-static值定义在概要文件是由使用的解析器,这将在本系列以后的文章中讨论),而不是从claims-bag中提取的变量值。每种技术概要文件类型都定义了一组不同的必需和可选元数据.
Self-Asserted Technical Profiles要求您设置ContentDefinitionReferenceId元数据值。这个值应该指示在策略的“构建块”部分指定的内容定义的ID,并告诉Self-Asserted Technical Profile如何呈现此页面。
可以为Self-Asserted Technical Profiles设置其他几个元数据值。其中一些仅对特定的页面布局类型和版本有效。要查看可以设置的其他值,请参阅在线文档中的Self-Asserted Technical Profile Metadata部分。

Updating Your Policy

在包含理赔转换技术概要的理赔提供方之后,将以下自我断言的理赔提供方添加到您的政策中:

<ClaimsProvider><!--The technical profile defined in this section allows a user to enter their first and last name, then composes the message text based on those entries.--><DisplayName>Sample User Input Collection Technical Profiles</DisplayName><TechnicalProfiles><TechnicalProfile Id="UserInformationCollector"><DisplayName>Collect Sample User Input Technical Profile</DisplayName><Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /><Metadata><Item Key="ContentDefinitionReferenceId">SelfAssertedContentDefinition</Item></Metadata><DisplayClaims><DisplayClaim ClaimTypeReferenceId="givenName" Required="true"/><DisplayClaim ClaimTypeReferenceId="surname"  Required="true"/></DisplayClaims><OutputClaims><OutputClaim ClaimTypeReferenceId="givenName"/><OutputClaim ClaimTypeReferenceId="surname"/></OutputClaims></TechnicalProfile></TechnicalProfiles>
</ClaimsProvider>

通过Protocol元素将Technical Profile设置为Self-Asserted类型,该元素将Name设置为Proprietary,并将处理程序设置为SelfAssertedAttributeProvider程序集。元数据部分设置ContentDefinitionReferenceId值来引用你之前创建的SelfAssertedContentDefinition。
Technical Profile指定了两个DisplayClaim元素,一个用于givenName和姓氏索赔。这两个声明都被标记为Required,以迫使用户在完成页面之前提供这些值。

Other Technical Profiles We Have Used So Far

在本系列的前一篇文章中,您包含了JWT令牌颁发者技术概要文件的一个实例。与我们刚才讨论的索赔交换和自我断言技术概要文件不同,它具有不寻常的协议值None,以及设置为JWT的OutputTokenFormat值。它还不同于那些Technical Profiles,因为它不使用任何输入或输出声明。然而,它确实需要通过Metadata和CryptographicKeys元素设置参数。JwtIssuer技术简介复制如下以供参考:

<TechnicalProfile Id="JwtIssuer"><DisplayName>JWT Issuer</DisplayName><Protocol Name="None" /><OutputTokenFormat>JWT</OutputTokenFormat><Metadata><Item Key="client_id">{service:te}</Item><Item Key="issuer_refresh_token_user_identity_claim_type">objectId</Item><Item Key="SendTokenResponseBodyWithJsonNumbers">true</Item></Metadata><CryptographicKeys><Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" /><Key Id="issuer_refresh_token_key" StorageReferenceId="B2C_1A_TokenEncryptionKeyContainer" /></CryptographicKeys><InputClaims /><OutputClaims />
</TechnicalProfile>

您已经看到了在Self-Asserted Technical Profile中使用的Metadata元素。CryptographicKeys元素用于提供一个技术概要文件,其中引用存储在您的AAD B2C实例中的策略旁边的一个或多个策略密钥。这些策略密钥可用于数字签名和加密操作,以及记录应用程序秘密(如令牌访问密钥)。AAD B2C支持为您生成密钥或允许您自己指定密钥。

请注意
实际上还有一个索赔提供方技术简介,您已经看到了。在本系列的第一篇文章中,您包含了一个必需的技术概要文件,其ID为TpEngine_c3bd4fe2-1775-4013-b91d-35f16d377d13。这是一个特殊的Technical Profile,它的行为与您将用于实现自己的策略逻辑的Technical Profile略有不同。

Sequencing the Steps of the User Journey

现在我们有了一些函数,理所当然地,我们可能需要一些函数来调用它们。策略执行的步骤需要在UserJourneys部分中指定。想想这个部分是在说“当我的用户调用我的策略时,这是我希望他们采取的旅程。”UserJourneys元素可以包含一个或多个UserJourney子元素,每个UserJourney描述不同的用户活动,例如登录、注册、重置密码、编辑配置文件等等。当您使用概要文件的层次结构时,更常见的情况是在概要文件中指定多个用户行程,我们将在本系列的后面讨论这个问题。
每个UserJourney元素必须有一个分配给它的唯一ID,这是旅程稍后被引用的方式。每个User Journey中的各个步骤在OrchestrationSteps元素中指定,该元素包含一个或多个单独的OchestrationStep元素。然后,业务流程步骤元素包含描述每个步骤的执行顺序(以“1”开头)和被调用的步骤类型的属性。根据所使用的业务流程步骤的类型,可能需要更多信息。
OrchestrationStep元素还可以包含一组先决条件,可以对这些条件进行评估,以确定是否应该跳过该步骤。我们将在本系列稍后的Hello!请登录或注册张贴。

Calling your Technical Profiles in the User Journey

将HelloWorldJourney User Journey的现有内容替换为以下业务流程步骤集:

<OrchestrationSteps><OrchestrationStep Order="1" Type="ClaimsExchange"><ClaimsExchanges><ClaimsExchange Id="GetObjectIdClaimsExchange" TechnicalProfileReferenceId="RandomObjectIdClaimGenerator" /></ClaimsExchanges></OrchestrationStep><OrchestrationStep Order="2" Type="ClaimsExchange"><ClaimsExchanges><ClaimsExchange Id="GetUserInformationClaimsExchange" TechnicalProfileReferenceId="UserInformationCollector" /></ClaimsExchanges></OrchestrationStep><OrchestrationStep Order="3" Type="ClaimsExchange"><ClaimsExchanges><ClaimsExchange Id="GetMessageClaimsExchange" TechnicalProfileReferenceId="UserInputMessageClaimGenerator"/></ClaimsExchanges></OrchestrationStep><OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>

HelloWorldJourney现在包含四个步骤,其中三个步骤的类型值为ClaimsExchange。Claims Exchange步骤类型用于大多数步骤类型,只有少数例外,我们将在后面讨论。当您指定一个ClaimsExchange Orchestration步骤时,您必须在该步骤中包含一个ClaimsExchanges元素。索赔交换步骤类型,您需要包括一个ClaimsExchange元素(参见下面注意例外指导),有一个自己的ID,以及TechnicalProfile元素的ID引用你想执行这个步骤。

Sending the JWT Token When the User Journey Completes

这个步骤不是一个Claims Exchange步骤,而是指定SendClaims的类型。Send Claims Orchestration步骤负责从策略返回包含所需索赔的令牌。它的独特之处在于,它不是使用ClaimsExchange元素来引用它调用的技术概要文件,而是使用一个名为CpimIssuerTechnicalProfileReferenceId的属性来引用JWT令牌颁发者技术概要文件的ID。

Updating the Output Claims in the Relying Party

将保单中依赖方部分的输出索赔部分替换为以下内容:

<OutputClaims><OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" /><OutputClaim ClaimTypeReferenceId="displayName" /><OutputClaim ClaimTypeReferenceId="message" />
</OutputClaims>

对于这个更改,有两个关键的事情需要注意。首先,我们不再使用Default Values设置索赔值。我们可以把它们留在那里,但它们不再是必要的,因为我们的索赔值是由用户旅程中的技术配置文件设置的。其次,我们已经将displayName声明添加到将由我们的策略生成的JWT令牌中添加的声明集。

Azure - AD B2C自定义策略和身份体验框架相关推荐

  1. 【壹刊】Azure AD B2C(一)初识

    一,引言(上节回顾) 上一节讲到Azure AD的一些基础概念,以及如何运用 Azure AD 包含API资源,Azure AD 是微软提供的云端的身份标识和资源访问服务,帮助员工/用户/管理员访问一 ...

  2. 使用Azure AD B2C为ASP.NET Core 设置登录/注册

    一,引言 上次关于Azure AD B2C 讲到一些概念,有介绍到,Azure AD B2C 也是一种身份验证的解决方案,但是它运行客户使用其首选的社交,企业或者本地账户标识对应用程序和API进行单一 ...

  3. Azure AD B2C

    目录 1. 什么是OAuth 2. 什么是OpenID Connect 3. 什么是Azure AD B2C 4.SPA中应用Azure AD B2C进行授权 5. 重要的claim讲解

  4. ASP.NET Core和Blazor Code Venture:配置Azure AD身份验证

    目录 介绍 背景 安装开发环境 先决条件 安装框架和工具 安装Azure Active Directory 创建一个新用户 使用Azure AD进行服务器端Blazor身份验证 第一次运行 下载ToD ...

  5. Azure: Azure AD(For Development)的使用

    一.简介 (一)Azure AD 简介 Azure Active Directory (Azure AD) 是 Microsoft 推出的基于云的全面的标识和访问管理服务,它将核心目录服务.高级标识监 ...

  6. ZA303学习笔记九部署和管理Azure计算资源 Azure AD/配置MFA

    Azure AD 一:添加自定义域名 配置Azure AD身份保护(Identity Protection) 风险检测 登录风险 用户风险 许可证要求 二:Azure AD Privileged Id ...

  7. 一文读懂Auth0与Azure AD的区别

    随着软件即服务(SaaS)模式逐渐普及,开发人员开始寻找更易于管理流程的方法.其中一种途径是使用客户身份验证和访问管理(CIAM)解决方案来减轻管理最终用户数据库的工作量.Auth0 和微软 Azur ...

  8. 【壹刊】Azure AD 保护的 ASP.NET Core Web API (下)

    一,引言 上一节讲到如何在我们的项目中集成Azure AD 保护我们的API资源,以及在项目中集成Swagger,并且如何把Swagger作为一个客户端进行认证和授权去访问我们的WebApi资源的?本 ...

  9. 通过Azure AD 搭建企业安全身份标识系统

    最近,"安全"成为行业最热的词,而身份安全在企业上云背景下,也在逐渐成为企业基础架构不可或缺的一部分.但提到身份安全,仍然有不少用户并不了解该从何下手.今天我们将邀请微软数字化转型 ...

最新文章

  1. 【基础知识】如何快速转发CSDN博客
  2. 骑行广州大学城 外环十大校门
  3. 归并排序的基本原理及实现
  4. 模拟实现ArrayList与 LinkedList
  5. Qt Creator操作方法
  6. pandas之时间数据
  7. C++学习之CodeBlocks安装与调试
  8. iOS汉字转拼音,日韩文字转拼音
  9. idle运行python_命令行启动python的IDLE
  10. 【渝粤教育】国家开放大学2019年春季 1117机电控制与可编程序控制 参考试题
  11. Opencv椭圆拟合
  12. 2020年中国网络安全产业白皮书分析解读
  13. 数据分析中的统计学基础知识
  14. 第七版(谢希仁)计算机网络 知识点总结
  15. AutoIt教程资源汇总
  16. ArcGIS学习——菜单栏
  17. OA新增百亿市场,蓝凌、钉钉靠“智能OA”占稳C位
  18. Java图标对应的文件类型
  19. MSL(湿气等级)2020-09-29
  20. AD10 自动布线的问题

热门文章

  1. 孙陶然:成功者都不找借口
  2. 用户注册进行密码加密MD5
  3. 计算机病毒的入侵路径,计算机病毒的入侵方式有哪些?
  4. 2022电大国家开放大学网上形考任务-桥梁工程技术非免费(非答案)
  5. JAVA架构之路(数据加密与常见加密算法)
  6. 华为项目管理10大模板Excel版(可直接套用_非常实用)
  7. [渝粤教育] 西南科技大学 国际经济法 在线考试复习资料
  8. mac 3分钟装好Vue(全程命令行)
  9. 【M】⽴项or申报书中的重点难点咋写?
  10. 杜笙除镍树脂Tulsimer CH-90实际运用案例