使用 Azure Active Directory B2C 自定义策略收集和操作用户输入
借助 Azure Active Directory B2C (Azure AD B2C) 自定义策略,可以收集用户输入。 然后,可以使用内置方法来操作用户输入。
在本文中,你将了解如何通过图形用户界面写入收集用户输入的自定义策略。 然后,你将访问输入、对其进行处理,最后将它们作为 JWT 令牌中的声明返回。 要完成此任务,你将:
发布声明。 声明可在 Azure AD B2C 策略执行过程中提供数据的临时存储。 它可存储用户的相关信息,例如名字、姓氏,或者从用户或其他系统获得的任何其他声明。 可以在 Azure AD B2C 自定义策略概述中详细了解声明。
定义技术配置文件。 技术配置文件提供了与不同类型的参与方进行通信的接口。 例如,它允许你与用户交互以收集数据。
配置声明转换,用于操作所发布的声明。
配置内容定义。 内容定义可定义要加载的用户界面。 稍后,可以通过提供自己的自定义 HTML 内容来自定义用户界面。
使用自断言技术配置文件及 DisplayClaims 来配置用户界面并向用户显示。
使用业务流程步骤按给定顺序调用技术配置文件。
先决条件
如果没有租户,请创建链接到 Azure 订阅的 Azure AD B2C 租户。
必须在计算机上安装 Visual Studio Code (VS Code)。
完成编写第一个 Azure AD B2C 自定义策略 - Hello World!中的步骤。 本文是创建和运行自己的自定义策略操作指南系列教程的一部分。
注意
本文是《在 Azure Active Directory B2C 中创建和运行自己的自定义策略操作指南系列教程》的一部分。 建议从第一篇文章开始本系列教程。
步骤 1 - 发布声明
将其他声明与 objectId 和 message 一起发布:
在 VS Code 中,打开
ContosoCustomPolicy.XML
文件。在
ClaimsSchema
部分中,添加以下 ClaimType 声明:<ClaimType Id="givenName"> <DisplayName>Given Name</DisplayName> <DataType>string</DataType> <UserHelpText>Your given name (also known as first name).</UserHelpText> <UserInputType>TextBox</UserInputType> </ClaimType> <ClaimType Id="surname"> <DisplayName>Surname</DisplayName> <DataType>string</DataType> <UserHelpText>Your surname (also known as family name or last name).</UserHelpText> <UserInputType>TextBox</UserInputType> </ClaimType> <ClaimType Id="displayName"> <DisplayName>Display Name</DisplayName> <DataType>string</DataType> <UserHelpText>Your display name.</UserHelpText> <UserInputType>TextBox</UserInputType> </ClaimType>
我们已声明三种声明类型:givenName、surname 和 displayName。 这些声明包括 DataType
、UserInputType
和 DisplayName
元素:
- DataType 指定了声明保留的值的数据类型。 详细了解 DataType 元素支持的数据类型。
- 如果想要从用户收集声明的值,UserInputType 指定了用户界面上显示的 UI 控件。 详细了解 Azure AD B2C 支持的用户输入类型。
- 如果要从用户收集声明的值,DisplayName 指定了用户界面上显示的 UI 控件的标签。
步骤 2 - 定义声明转换
ClaimsTransformation 包含用于将给定声明转换为另一个声明的函数。 例如,可以将字符串声明从小写更改为大写。 详细了解 Azure AD B2C 支持的声明转换。
在
ContosoCustomPolicy.XML
文件中,添加<ClaimsTransformations>
元素作为BuildingBlocks
部分的子级。<ClaimsTransformations> </ClaimsTransformations>
在
ClaimsTransformations
元素中添加以下代码:<ClaimsTransformation Id="GenerateRandomObjectIdTransformation" 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>
我们配置了三种声明转换:
GenerateRandomObjectIdTransformation 可生成由 CreateRandomString 方法指定的随机字符串。 objectId 声明可使用由
OutputClaim
元素生成的字符串进行更新。CreateDisplayNameTransformation 可连接 givenName 和 surname,以构成 displayName 格式。
CreateMessageTransformation 可连接 Hello 和 displayName,以构成 消息。
步骤 3 - 配置内容定义
使用 ContentDefinitions,可以指定 HTML 模板的 URL,这些模板控制你向用户显示的网页布局。 可以为每个步骤指定特定的用户界面,例如登录或注册、密码重置或错误页。
要添加内容定义,请在 ContosoCustomPolicy.XML
文件的 BuildingBlocks
部分中添加以下代码:
<ContentDefinitions>
<ContentDefinition Id="SelfAssertedContentDefinition">
<LoadUri>~/tenant/templates/AzureBlue/selfAsserted.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:2.1.7</DataUri>
</ContentDefinition>
</ContentDefinitions>
步骤 4 - 配置技术配置文件
在自定义策略中,TechnicalProfile 是实现功能的元素。 现在,你已定义声明和声明转换,需要技术配置文件来执行定义。 技术配置文件在 ClaimsProvider
元素中声明。
Azure AD B2C 提供了一组技术配置文件。 每个技术配置文件可执行特定角色。 例如,使用 REST 技术配置文件可对服务终结点进行 HTTP 调用。 可以使用声明转换技术配置文件来执行在声明转换中定义的操作。 详细了解 Azure AD B2C 自定义策略提供的 技术配置文件类型。
设置声明的值
要设置 objectId、displayName 和 message 声明的值,请配置执行 GenerateRandomObjectIdTransformation、CreateDisplayNameTransformation 和 CreateMessageTransformation 声明转换的技术配置文件。 声明转换按在 OutputClaimsTransformations
元素中定义的顺序执行。 例如,它首先创建显示名称,然后创建消息。
将以下
ClaimsProvider
添加为ClaimsProviders
部分的子项。<ClaimsProvider> <DisplayName>Technical Profiles to generate claims</DisplayName> </ClaimsProvider>
要设置 objectId、displayName 和 message 声明的值,请在刚刚创建的
ClaimsProvider
元素中添加以下代码:<!--<ClaimsProvider>--> <TechnicalProfiles> <TechnicalProfile Id="ClaimGenerator"> <DisplayName>Generate Object ID, displayName and message Claims 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"/> <OutputClaim ClaimTypeReferenceId="displayName"/> <OutputClaim ClaimTypeReferenceId="message"/> </OutputClaims> <OutputClaimsTransformations> <OutputClaimsTransformation ReferenceId="GenerateRandomObjectIdTransformation"/> <OutputClaimsTransformation ReferenceId="CreateDisplayNameTransformation"/> <OutputClaimsTransformation ReferenceId="CreateMessageTransformation"/> </OutputClaimsTransformations> </TechnicalProfile> </TechnicalProfiles> <!--</ClaimsProvider>-->
收集用户输入
你会根据 givenName 和 surname 生成 displayName 声明,因此需要收集它们作为用户输入。 要收集用户输入,请使用名为 Self-Asserted 的技术配置文件类型。 配置自断言技术配置文件时,由于自断言技术配置文件负责显示用户界面,因此需要引用内容定义。
将以下
ClaimsProvider
添加为ClaimsProviders
部分的子项。<ClaimsProvider> <DisplayName>Technical Profiles to collect user's details </DisplayName> </ClaimsProvider>
在刚刚创建的
ClaimsProvider
元素中添加以下代码:<TechnicalProfiles> <TechnicalProfile Id="UserInformationCollector"> <DisplayName>Collect 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>
请注意 givenName 和 surname 声明的两个显示声明。 这两个声明都标记为必需,因此用户必须先输入值,然后才能提交向其显示的表单。 声明按照 DisplayClaims 元素中定义的顺序显示在屏幕上,例如,首先显示名字,然后显示姓氏。
步骤 5 - 定义用户旅程
使用用户旅程定义调用技术配置文件的顺序。 使用 OrchestrationSteps
元素指定用户旅程中的步骤。
将 HelloWorldJourney
用户旅程的现有内容替换为以下代码:
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="GetUserInformationClaimsExchange" TechnicalProfileReferenceId="UserInformationCollector"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="GetMessageClaimsExchange" TechnicalProfileReferenceId="ClaimGenerator"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer"/>
</OrchestrationSteps>
根据业务流程步骤,我们收集用户输入,设置 objectId、displayName 和 message 声明的值,最后发送 Jwt 令牌。
步骤 6 - 更新信赖方
将 RelyingParty
部分的 OutputClaims
元素内容替换为以下代码:
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="displayName"/>
<OutputClaim ClaimTypeReferenceId="message"/>
完成步骤 6 后,ContosoCustomPolicy.XML
文件外观应类似于以下代码:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TrustFrameworkPolicy xmlns: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="yourtenant.partner.onmschina.cn"
PolicyId="B2C_1A_ContosoCustomPolicy"
PublicPolicyUri="http://yourtenant.partner.onmschina.cn/B2C_1A_ContosoCustomPolicy">
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="objectId">
<DisplayName>unique object Id for subject of the claims being returned</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="message">
<DisplayName>Will hold Hello World message</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="givenName">
<DisplayName>Given Name</DisplayName>
<DataType>string</DataType>
<UserHelpText>Your given name (also known as first name).</UserHelpText>
<UserInputType>TextBox</UserInputType>
</ClaimType>
<ClaimType Id="surname">
<DisplayName>Surname</DisplayName>
<DataType>string</DataType>
<UserHelpText>Your surname (also known as family name or last name).</UserHelpText>
<UserInputType>TextBox</UserInputType>
</ClaimType>
<ClaimType Id="displayName">
<DisplayName>Display Name</DisplayName>
<DataType>string</DataType>
<UserHelpText>Your display name.</UserHelpText>
<UserInputType>TextBox</UserInputType>
</ClaimType>
</ClaimsSchema>
<ClaimsTransformations>
<ClaimsTransformation Id="GenerateRandomObjectIdTransformation" 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>
<ContentDefinitions>
<ContentDefinition Id="SelfAssertedContentDefinition">
<LoadUri>~/tenant/templates/AzureBlue/selfAsserted.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:2.1.7</DataUri>
</ContentDefinition>
</ContentDefinitions>
</BuildingBlocks>
<!--Claims Providers Here-->
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Token Issuer</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>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<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>
<DisplayName>Claim Generator Technical Profiles</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="ClaimGenerator">
<DisplayName>Generate Object ID, displayName and message Claims 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"/>
<OutputClaim ClaimTypeReferenceId="displayName"/>
<OutputClaim ClaimTypeReferenceId="message"/>
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="GenerateRandomObjectIdTransformation"/>
<OutputClaimsTransformation ReferenceId="CreateDisplayNameTransformation"/>
<OutputClaimsTransformation ReferenceId="CreateMessageTransformation"/>
</OutputClaimsTransformations>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Technical Profiles to collect user's details</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="UserInformationCollector">
<DisplayName>Collect 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>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="HelloWorldJourney">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="GetUserInformationClaimsExchange" TechnicalProfileReferenceId="UserInformationCollector"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="GetMessageClaimsExchange" TechnicalProfileReferenceId="ClaimGenerator"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer"/>
</OrchestrationSteps>
</UserJourney>
</UserJourneys>
<RelyingParty><!--
Relying Party Here that's your policy’s entry point
Specify the User Journey to execute
Specify the claims to include in the token that is returned when the policy runs
-->
<DefaultUserJourney ReferenceId="HelloWorldJourney"/>
<TechnicalProfile Id="HelloWorldPolicyProfile">
<DisplayName>Hello World Policy Profile</DisplayName>
<Protocol Name="OpenIdConnect"/>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="displayName"/>
<OutputClaim ClaimTypeReferenceId="message"/>
</OutputClaims>
<SubjectNamingInfo ClaimType="sub"/>
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
如果尚未这样做,请将 yourtenant
替换为租户名称的子域部分,例如 contoso
。 了解如何获取租户名称。
步骤 7 - 上传自定义策略文件
按照上传自定义策略文件中的步骤进行操作。 如果要上传与门户中已有文件同名的文件,请确保选择“覆盖自定义策略(如果已存在)”。
步骤 8 - 测试自定义策略
在“自定义策略”下,选择“B2C_1A_CONTOSOCUSTOMPOLICY”。
对于自定义策略概述页面上的“选择应用程序”,选择 Web 应用程序,例如之前注册的 webapp1。 确保将“选择回复 URL”的值设置为“
https://jwt.ms
”。单击“立即运行”按钮。
输入名和姓,然后选择“继续”。
策略执行完成后,系统会将你重定向到 https://jwt.ms
,然后你会看到已解码的 JWT 令牌。 其外观类似于以下 JWT 令牌代码片段:
{
"typ": "JWT",
"alg": "RS256",
"kid": "pxLOMWFg...."
}.{
...
"sub": "c7ae4515-f7a7....",
...
"acr": "b2c_1a_contosocustompolicy",
...
"name": "Maurice Paulet",
"message": "Hello Maurice Paulet"
}.[Signature]
后续步骤
接下来,了解:
Azure AD B2C 自定义策略中的技术配置文件类型。