Partilhar via


Coletar e manipular entradas de usuário usando a política personalizada do Azure Ative Directory B2C

As políticas personalizadas do Azure Ative Directory B2C (Azure AD B2C) permitem coletar entradas do usuário. Em seguida, você pode usar métodos embutidos para manipular as entradas do usuário.

Neste artigo, você aprenderá a escrever uma política personalizada que coleta entradas do usuário por meio de uma interface gráfica do usuário. Em seguida, você acessará as entradas, processará e, finalmente, as retornará como declarações em um token JWT. Para concluir esta tarefa, você:

  • Declarar reivindicações. Uma declaração fornece armazenamento temporário de dados durante a execução de uma política do Azure AD B2C. Ele pode armazenar informações sobre o usuário, como nome, sobrenome ou qualquer outra reivindicação obtida do usuário ou de outros sistemas. Você pode saber mais sobre declarações na visão geral da política personalizada do Azure AD B2C.

  • Definir perfis técnicos. Um perfil técnico fornece uma interface para comunicar com diferentes tipos de partes. Por exemplo, ele permite que você interaja com o usuário para coletar dados.

  • Configure transformações de declarações, que você usa para manipular as declarações declaradas.

  • Configure definições de conteúdo. Uma definição de conteúdo define a interface do usuário a ser carregada. Mais tarde, você pode personalizar a interface do usuário fornecendo seu próprio conteúdo HTML personalizado.

  • Configure e mostre interfaces de usuário para o usuário usando Self-Asserted Technical Profiles e DisplayClaims.

  • Chame perfis técnicos em uma determinada sequência usando as etapas de orquestração.

Pré-requisitos

Nota

Este artigo faz parte da série de guias de instruções Criar e executar suas próprias políticas personalizadas no Azure Ative Directory B2C. Recomendamos que comece esta série a partir do primeiro artigo.

Etapa 1 - Declarar reclamações

Declare declarações adicionais ao lado de objectId e message:

  1. No VS Code, abra o ContosoCustomPolicy.XML arquivo.

  2. ClaimsSchema Na seção , adicione as seguintes declarações 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>
    

Declaramos três tipos de reivindicação, givenName, sobrenome e displayName. Estas declarações incluem DataType, UserInputType e DisplayName elementos:

  • DataType especifica o tipo de dados do valor que as declarações mantêm. Saiba mais sobre os tipos de dados suportados pelos elementos DataType.
  • UserInputType especifica o controle de interface do usuário que aparece na interface do usuário se você quiser coletar o valor da declaração do usuário. Saiba mais sobre os tipos de entrada de usuário suportados pelo Azure AD B2C.
  • DisplayName especifica o rótulo para o controle de interface do usuário que aparece na interface do usuário se você quiser coletar o valor da declaração do usuário.

Etapa 2 - Definir transformações de declarações

Um ClaimsTransformation contém uma função que você usa para converter uma determinada declaração em outra. Por exemplo, você pode alterar uma declaração de cadeia de caracteres de minúsculas para maiúsculas. Saiba mais sobre as transformações de declarações suportadas pelo Azure AD B2C.

  1. ContosoCustomPolicy.XML No arquivo, adicione um <ClaimsTransformations> elemento como filho da BuildingBlocks seção.

        <ClaimsTransformations>
    
        </ClaimsTransformations>
    
  2. Adicione o seguinte código dentro do ClaimsTransformations elemento :

        <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> 
    

    Configuramos três transformações de declarações:

    • GenerateRandomObjectIdTransformation gera uma cadeia de caracteres aleatória conforme especificado pelo método CreateRandomString . A declaração objectId é atualizada com a cadeia de caracteres gerada, OutputClaim conforme especificado pelo elemento .

    • CreateDisplayNameTransformation concatena givenName e sobrenome para formar displayName.

    • CreateMessageTransformation concatena Hello e displayName para formar mensagem.

Etapa 3 - Configurar definições de conteúdo

ContentDefinitions permite que você especifique URL para modelos HTML que controlam o layout das páginas da Web que você mostra aos usuários. Você pode especificar interfaces de usuário específicas para cada etapa, como entrada ou inscrição, redefinição de senha ou páginas de erro.

Para adicionar a definição de conteúdo, adicione o seguinte código na BuildingBlocks seção do ContosoCustomPolicy.XML arquivo:

    <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>

Etapa 4 - Configurar perfis técnicos

Em uma política personalizada, um TechnicalProfile é o elemento que implementa a funcionalidade. Agora que você definiu Declarações e Transformações de Declarações, precisará de Perfis Técnicos para executar suas definições. Um perfil técnico é declarado dentro dos ClaimsProvider elementos.

O Azure AD B2C fornece um conjunto de perfis técnicos. Cada perfil técnico desempenha uma função específica. Por exemplo, você usa um perfil técnico REST para fazer uma chamada HTTP para um ponto de extremidade de serviço. Você pode usar um perfil técnico de transformação de declarações para executar a operação definida em uma transformação de declarações. Saiba mais sobre os tipos de perfis técnicos que as políticas personalizadas do Azure AD B2C fornecem.

Defina valores para as suas reclamações

Para definir valores para declarações objectId, displayName e message, configure um perfil técnico que execute as transformações de declarações GenerateRandomObjectIdTransformation, CreateDisplayNameTransformation e CreateMessageTransformation. As transformações de declarações são executadas pela ordem definida no OutputClaimsTransformations elemento . Por exemplo, ele primeiro cria o nome para exibição e, em seguida, a mensagem.

  1. Adicione o seguinte ClaimsProvider como filho da ClaimsProviders seção.

        <ClaimsProvider>
    
            <DisplayName>Technical Profiles to generate claims</DisplayName>
        </ClaimsProvider>
    
    
  2. Para definir valores para objectId, displayName e declarações de mensagem, adicione o seguinte código dentro do elemento que você acabou de ClaimsProvider criar:

        <!--<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>-->
    

Coletar entradas do usuário

Você gera a declaração displayName de givenName e sobrenome, então você precisa coletar então como entradas do usuário. Para coletar uma entrada do usuário, use um tipo de perfil técnico chamado Self-Asserted. Ao configurar um perfil técnico autodeclarado, você precisa fazer referência às definições de conteúdo, pois o perfil técnico autodeclarado é responsável por exibir uma interface do usuário.

  1. Adicione o seguinte ClaimsProvider como filho da ClaimsProviders seção.

        <ClaimsProvider>
    
            <DisplayName>Technical Profiles to collect user's details </DisplayName>
        </ClaimsProvider>
    
  2. Adicione o seguinte código dentro do elemento que ClaimsProvider você acabou de criar:

        <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>
    

    Observe que as duas declarações de exibição para as declarações givenName e sobrenome . Ambas as declarações são marcadas como obrigatórias, portanto, o usuário deve inserir os valores antes de enviar o formulário exibido a eles. As declarações são exibidas na tela na ordem definida no elemento DisplayClaims, como o Nome Próprio e, em seguida, o Sobrenome.

Etapa 5 - Definir as jornadas do usuário

Você usa jornadas de usuário para definir a ordem em que os perfis técnicos são chamados. Use o OrchestrationSteps elemento para especificar as etapas em uma jornada do usuário.

Substitua o conteúdo existente da HelloWorldJourney Jornada do Usuário pelo seguinte código:

    <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>

De acordo com as etapas de orquestração, coletamos entradas do usuário, definimos valores para objectId, displayName e declarações de mensagem e, finalmente, enviamos o token Jwt.

Etapa 6 - Atualizar a terceira parte confiável

Substitua o OutputClaims conteúdo do elemento da RelyingParty seção pelo seguinte código:

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

Depois de concluir a etapa 6, o arquivo deve ser semelhante ao código a ContosoCustomPolicy.XML seguir:

<?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.onmicrosoft.com" 
    PolicyId="B2C_1A_ContosoCustomPolicy" 
    PublicPolicyUri="http://yourtenant.onmicrosoft.com/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>

Se ainda não tiver feito isso, substitua pela parte do subdomínio yourtenant do nome do locatário, como contoso. Saiba como Obter o nome do seu inquilino.

Etapa 3 - Carregar arquivo de política personalizado

Siga as etapas em Carregar arquivo de política personalizado. Se você estiver carregando um arquivo com o mesmo nome que o que já está no portal, certifique-se de selecionar Substituir a política personalizada, se ela já existir.

Etapa 4 - Testar a política personalizada

  1. Em Políticas personalizadas, selecione B2C_1A_CONTOSOCUSTOMPOLICY.

  2. Para Selecionar aplicativo na página de visão geral da política personalizada, selecione o aplicativo Web, como webapp1 , que você registrou anteriormente. Certifique-se de que o valor Select reply URL está definido comohttps://jwt.ms.

  3. Selecione o botão Executar agora .

  4. Introduza Nome Próprio e Apelido e, em seguida, selecione Continuar.

    screenshot of accepting user inputs in custom policy.

Depois que a política terminar a execução, você será redirecionado para https://jwt.mso , e verá um token JWT decodificado. Ele é semelhante ao seguinte trecho de token JWT:

    {
      "typ": "JWT",
      "alg": "RS256",
      "kid": "pxLOMWFg...."
    }.{
      ...
      "sub": "c7ae4515-f7a7....",
      ...
      "acr": "b2c_1a_contosocustompolicy",
      ...
      "name": "Maurice Paulet",
      "message": "Hello Maurice Paulet"
    }.[Signature]

Próximos passos

A seguir, aprenda: