修改邮箱时检查新邮箱是否已存在?当前配置逻辑异常排查
问题分析
当前逻辑的核心问题:
AAD-UserExistUsingEmailCheck仅读取邮箱对应的用户ID,但未判断该ID是否为当前用户,且未主动抛出邮箱已存在的错误。- 验证码验证的前置条件逻辑错误:当检测到邮箱存在时跳过验证,这会导致邮箱被占用时反而不验证验证码,也不报错。
- 未在验证环节触发错误提示,而是依赖后续的ValidationTechnicalProfiles,不符合“点击验证时就抛出错误”的需求。
修复方案
1. 添加声明转换判断邮箱归属
先添加一个ClaimsTransformation,用于对比返回的emailObjectId和当前用户的objectId:
<ClaimsTransformation Id="CheckEmailObjectIdMatchesCurrentUser" TransformationMethod="CompareClaimStrings"> <InputClaims> <InputClaim ClaimTypeReferenceId="objectId" TransformationClaimType="inputClaim1" /> <InputClaim ClaimTypeReferenceId="emailObjectId" TransformationClaimType="inputClaim2" /> </InputClaims> <InputParameters> <InputParameter Id="compareTo" DataType="string" Value="NOT_EQUAL" /> <InputParameter Id="outputClaimDataType" DataType="boolean" /> </InputParameters> <OutputClaims> <OutputClaim ClaimTypeReferenceId="emailAlreadyTaken" TransformationClaimType="outputClaim" /> </OutputClaims> </ClaimsTransformation>
同时在ClaimTypes里声明emailAlreadyTaken:
<ClaimType Id="emailAlreadyTaken"> <DisplayName>Email Already Taken</DisplayName> <DataType>boolean</DataType> </ClaimType>
2. 修改AAD-UserExistUsingEmailCheck技术配置文件
调整元数据,确保读取用户信息时,即使邮箱不存在也不报错,但需要返回当前用户的objectId用于对比:
<TechnicalProfile Id="AAD-UserExistUsingEmailCheck"> <Metadata> <Item Key="Operation">Read</Item> <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">false</Item> <Item Key="ReturnClaimsOnFailure">objectId</Item> </Metadata> <IncludeInSso>false</IncludeInSso> <InputClaims> <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" Required="true" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="emailObjectId" PartnerClaimType="objectId" /> <OutputClaim ClaimTypeReferenceId="objectId" /> </OutputClaims> <IncludeTechnicalProfile ReferenceId="AAD-Common" /> </TechnicalProfile>
3. 调整ChangeEmailVerificationControl的VerifyCode逻辑
修改验证流程:先检查邮箱是否存在,然后判断是否被其他用户占用,若占用则抛出错误,否则继续验证验证码:
<DisplayControl Id="ChangeEmailVerificationControl" UserInterfaceControlType="VerificationControl"> <!-- 其他配置保持不变 --> <Actions> <Action Id="SendCode"> <!-- 原SendCode逻辑不变 --> </Action> <Action Id="VerifyCode"> <ValidationClaimsExchange> <!-- 1. 检查邮箱是否存在 --> <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="AAD-UserExistUsingEmailCheck" ContinueOnError="false" /> <!-- 2. 执行声明转换,判断邮箱是否被其他用户占用 --> <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="ClaimsTransformation-CheckEmailAlreadyTaken" ContinueOnError="false" /> <!-- 3. 验证验证码:仅当邮箱未被占用时执行 --> <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="VerifyEmailOtp"> <Preconditions> <Precondition Type="ClaimEquals" ExecuteActionsIf="true"> <Value>emailAlreadyTaken</Value> <Value>true</Value> <Action>SkipThisValidationTechnicalProfile</Action> </Precondition> </Preconditions> </ValidationClaimsExchangeTechnicalProfile> </ValidationClaimsExchange> </Action> </Actions> </DisplayControl>
需要添加对应的ClaimsTransformation技术配置文件:
<TechnicalProfile Id="ClaimsTransformation-CheckEmailAlreadyTaken"> <DisplayName>Check if email is already taken by another user</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <OutputClaims> <OutputClaim ClaimTypeReferenceId="emailAlreadyTaken" /> </OutputClaims> <ClaimsTransformations> <ClaimsTransformationReference Id="CheckEmailObjectIdMatchesCurrentUser" /> </ClaimsTransformations> <Metadata> <Item Key="UserMessageIfClaimsTransformationBooleanValueIsTrue">There is another user with this email address</Item> </Metadata> </TechnicalProfile>
4. 移除后续验证环节的重复检查
因为已经在VerifyCode步骤完成了邮箱占用检查,所以可以从LocalAccountWriteEmailUsingObjectId的ValidationTechnicalProfiles中移除重复检查:
<TechnicalProfile Id="LocalAccountWriteEmailUsingObjectId"> <!-- 其他配置保持不变 --> <ValidationTechnicalProfiles> <ValidationTechnicalProfile ReferenceId="AAD-UserWriteEmailUsingObjectId" /> <ValidationTechnicalProfile ReferenceId="SendEmailChangeEvent" /> </ValidationTechnicalProfiles> </TechnicalProfile>
关键逻辑说明
- 当用户输入验证码点击验证时,先查询目标邮箱对应的用户ID:
- 如果邮箱不存在,
emailObjectId为空,声明转换后emailAlreadyTaken为false,继续执行验证码验证。 - 如果邮箱存在,对比返回的用户ID和当前用户ID:
- 若一致(用户输入自己当前的邮箱),
emailAlreadyTaken为false,允许验证。 - 若不一致(邮箱被其他用户占用),
emailAlreadyTaken为true,触发错误提示,同时跳过验证码验证。
- 若一致(用户输入自己当前的邮箱),
- 如果邮箱不存在,
内容的提问来源于stack exchange,提问作者dani
相关产品推荐
相关产品推荐

