Proteger aplicaciones Java con Spring Boot mediante roles y declaraciones de rol

En este artículo se muestra una aplicación web de Java Spring Boot que usa la biblioteca cliente de Spring Boot Starter de Microsoft Entra ID para Java para la autenticación, autorización y adquisición de tokens. La aplicación usa el protocolo OpenID Connect para iniciar sesión de los usuarios y restringe el acceso a algunas rutas mediante roles de aplicación de Microsoft Entra ID (roles de aplicación) para la autorización.

Los roles de aplicación, junto con los grupos de seguridad, son medios populares para implementar la autorización. Con el control de acceso basado en roles (RBAC), mediante roles de aplicación y declaraciones de rol, puedes implementar de forma segura políticas de autorización con el mínimo esfuerzo. Otro enfoque sería usar los grupos de Microsoft Entra ID y las notificaciones de grupo. Los grupos de Microsoft Entra ID y los roles de aplicación no son mutuamente excluyentes. Puede usar ambos para proporcionar un control de acceso granular.

Para ver un vídeo que trata un escenario similar, consulte Implementación de la autorización en las aplicaciones mediante roles de aplicación, grupos de seguridad, ámbitos y roles de directorio.

Para obtener más información sobre cómo funcionan los protocolos en este y otros escenarios, consulte Autenticación frente a autorización.

En el siguiente diagrama se muestra la topología de la aplicación:

Diagrama que muestra la topología de la aplicación.

La aplicación usa la biblioteca cliente de Spring Boot Starter de Microsoft Entra ID para Java para iniciar sesión de un usuario y obtener un token de identificador de Microsoft Entra ID. El token de identificador contiene la notificación de roles. La aplicación examina el valor de esta declaración para determinar a qué páginas tiene autorización para acceder el usuario.

Este tipo de autorización se implementa mediante RBAC. Con RBAC, un administrador concede permisos a roles y no a usuarios individuales o grupos. El administrador, a continuación, puede asignar roles a diferentes usuarios y grupos para controlar quién tiene acceso a qué contenido y funcionalidad.

Esta aplicación de ejemplo define los dos roles de aplicación siguientes:

  • PrivilegedAdmin: se autoriza a acceder a las páginas Solo administradores y Usuarios normales.
  • RegularUser: Autorizado para acceder a la página Usuarios estándar.

Estos roles de aplicación se definen en Azure Portal en el manifiesto de registro de la aplicación. Cuando un usuario inicia sesión en la aplicación, Microsoft Entra ID emite una declaración de roles para cada rol asignado individualmente al usuario en forma de pertenencia al rol.

Puede asignar usuarios y grupos a roles con Azure Portal.

Nota:

Las notificaciones de rol no están presentes para los usuarios invitados en un inquilino si el punto de conexión https://login.microsoftonline.com/common/ se usa como autoridad para iniciar sesión de los usuarios. Necesita iniciar la sesión de un usuario en un punto de conexión de un tenant, como https://login.microsoftonline.com/tenantid.

Requisitos previos

Recomendaciones

  • Cierta familiaridad con Spring Framework.
  • Cierta familiaridad con el terminal Linux/OSX.
  • jwt.ms para inspeccionar los tokens.
  • Fiddler para supervisar la actividad de red y la solución de problemas.
  • Siga el blog de Microsoft Entra para mantenerse up-to-date con los últimos desarrollos.

Configuración del ejemplo

En las secciones siguientes se muestra cómo configurar la aplicación de ejemplo.

Clonación o descarga del repositorio de ejemplo

Para clonar el ejemplo, abra una ventana de Bash y use el siguiente comando:

git clone https://github.com/Azure-Samples/ms-identity-msal-java-samples.git
cd 4-spring-web-app/3-Authorization-II/roles

Como alternativa, vaya al repositorio ms-identity-msal-java-samples y, a continuación, descárguelo como un archivo .zip y extráigalo en el disco duro.

Importante

Para evitar las limitaciones de longitud de las rutas de archivo en Windows, clone o extraiga el repositorio en un directorio cercano a la raíz del disco duro.

Registre la aplicación de ejemplo en su tenant de Microsoft Entra ID

En este ejemplo hay un proyecto. En las secciones siguientes se muestra cómo registrar la aplicación mediante Azure Portal.

Elija el inquilinato de Microsoft Entra ID donde quiere crear las aplicaciones

Para elegir el inquilino, siga estos pasos:

  1. Inicie sesión en Azure Portal.

  2. Si su cuenta está presente en más de un directorio de Microsoft Entra ID, seleccione su perfil en una esquina del portal de Azure y, a continuación, seleccione Cambiar directorio para cambiar su sesión al directorio de Microsoft Entra ID deseado.

Registrar la aplicación (java-spring-webapp-roles)

Para registrar la aplicación, siga estos pasos:

  1. Vaya a Azure Portal y seleccione Microsoft Entra ID.

  2. Seleccione Registros de aplicaciones en el panel de navegación y, a continuación, seleccione Nuevo registro.

  3. En la página Registrar una aplicación que aparece, escriba la siguiente información de registro de la aplicación:

    • En la sección Nombre, escriba un nombre significativo de la aplicación, que se mostrará a los usuarios de la aplicación, por ejemplo, java-spring-webapp-roles.
    • En Tipos de cuenta admitidos, selecciona Solo las cuentas de este directorio organizativo.
    • En la sección URI de redirección (opcional), seleccione Web en el cuadro combinado y escriba los siguientes URI de redirección: http://localhost:8080/login/oauth2/code/.
  4. Seleccione Registrar para crear la aplicación.

  5. En la página de registro de la aplicación, busque y copie el valor del identificador de la aplicación (cliente) para usarlo más adelante. Este valor se usa en el archivo o los archivos de configuración de la aplicación.

  6. En la página de registro de la aplicación, seleccione Certificados y secretos en el panel de navegación para abrir la página donde puede generar secretos y cargar certificados.

  7. En la sección Secretos de cliente, seleccione Nuevo secreto de cliente.

  8. Escriba una descripción, por ejemplo, secreto de aplicación.

  9. Seleccione una expiración para el secreto o especifique una duración personalizada. Los secretos de cliente se limitan a una duración máxima de 24 meses y Microsoft recomienda una expiración de menos de 12 meses. Para las aplicaciones de producción, se recomienda usar un certificado o una credencial de identidad federada en lugar de un secreto de cliente.

  10. Seleccione Agregar. Se muestra el valor generado.

  11. Copie y guarde el valor generado para usarlo en pasos posteriores. Necesitas este valor para los archivos de configuración de tu código. Este valor no volverá a aparecer y no podrá recuperarlo por ningún otro medio. Por lo tanto, asegúrese de guardarlo desde Azure Portal antes de navegar a cualquier otra pantalla o panel.

Definición de roles para una aplicación

Use los pasos siguientes para definir los roles de la aplicación:

  1. Todavía en el mismo registro de aplicaciones, seleccione Roles de aplicación en el panel de navegación.

  2. Seleccione Crear rol de aplicación y, a continuación, escriba los valores siguientes:

    • En Nombre para mostrar, escriba un nombre adecuado; por ejemplo, PrivilegedAdmin.
    • En Tipos de miembros permitidos, elija Usuario.
    • Para Valor, escriba PrivilegedAdmin.
    • En Descripción, escriba PrivilegedAdmins quién puede ver la página de administración.
  3. Seleccione Crear rol de aplicación y, a continuación, escriba los valores siguientes:

    • En Nombre para mostrar, escriba un nombre adecuado; por ejemplo, RegularUser.
    • En Tipos de miembros permitidos, elija Usuario.
    • En el campo Valor, escriba RegularUser.
    • En Descripción, escriba RegularUsers que pueden ver la página de usuario.
  4. Seleccione Aplicar para guardar los cambios.

Asignación de usuarios a roles de aplicación

Para agregar usuarios al rol de aplicación definido anteriormente, siga las instrucciones que se indican aquí: Asignación de usuarios y grupos a roles.


Configure la aplicación (java-spring-webapp-roles) para usar tu registro de aplicaciones

Siga estos pasos para configurar la aplicación:

Nota:

En los pasos siguientes, ClientID es igual que Application ID o AppId.

  1. Abra el proyecto en su IDE.

  2. Abra el archivo src\main\resources\application.yml.

  3. Busque el marcador de posición Enter_Your_Tenant_ID_Here y reemplace el valor existente por el Id. de inquilino de Microsoft Entra.

  4. Busque el marcador de posición Enter_Your_Client_ID_Here y reemplace el valor existente por el identificador de aplicación o clientId de la aplicación java-spring-webapp-roles que ha copiado de Azure Portal.

  5. Busque el marcador de posición Enter_Your_Client_Secret_Here y reemplace el valor existente por el valor que guardó durante la creación de java-spring-webapp-roles, que copió del portal de Azure.

  6. Abra el archivo src/main/java/com/microsoft/azuresamples/msal4j/msidentityspringbootapplication/Sample.Controller.java.

  7. Busque las referencias a los roles de aplicación PrivilegedAdmin y RegularUser en este archivo. Si es necesario, cámbielos para reflejar los nombres de rol de la aplicación que eligió en los pasos anteriores.

Ejecución del ejemplo

En las secciones siguientes se muestra cómo implementar el ejemplo en Azure Container Apps.

Requisitos previos

Preparar el proyecto de Spring

Siga estos pasos para preparar el proyecto:

  1. Use el siguiente comando de Maven para compilar el proyecto:

    mvn clean verify
    
  2. Ejecute el proyecto de ejemplo localmente mediante el comando siguiente:

    mvn spring-boot:run
    

Configuración

Para iniciar sesión en Azure desde la CLI, ejecute el siguiente comando y siga las indicaciones para completar el proceso de autenticación.

az login

Para asegurarse de que ejecuta la versión más reciente de la CLI, ejecute el comando de actualización.

az upgrade

Luego, instale o actualice la extensión de Azure Container Apps para la CLI.

Si recibe errores sobre los parámetros que faltan al ejecutar comandos az containerapp en la CLI de Azure, asegúrese de tener instalada la versión más reciente de la extensión de Azure Container Apps.

az extension add --name containerapp --upgrade

Nota:

A partir de mayo de 2024, las extensiones de la CLI de Azure ya no habilitan las características en versión preliminar de forma predeterminada. Para acceder a las características de la versión preliminar de Container Apps, instale la extensión Container Apps con --allow-preview true.

az extension add --name containerapp --upgrade --allow-preview true

Ahora que la extensión o módulo actualizado está instalado, registre los espacios de nombre Microsoft.App y Microsoft.OperationalInsights.

Nota:

Los recursos de Azure Container Apps han migrado desde el espacio de nombres Microsoft.Web al espacio de nombres Microsoft.App. Consulte Migración del espacio de nombres de Microsoft.Web a Microsoft.App marzo de 2022 para obtener más detalles.

az provider register --namespace Microsoft.App
az provider register --namespace Microsoft.OperationalInsights

Creación del entorno de Azure Container Apps

Ahora que la configuración de la CLI de Azure está completa, puede definir las variables de entorno que se usan en este artículo.

Defina las siguientes variables en el shell de Bash.

export RESOURCE_GROUP="ms-identity-containerapps"
export LOCATION="canadacentral"
export ENVIRONMENT="env-ms-identity-containerapps"
export API_NAME="ms-identity-api"
export JAR_FILE_PATH_AND_NAME="./target/ms-identity-spring-boot-webapp-0.0.1-SNAPSHOT.jar"

Cree un grupo de recursos.

az group create  \
    --name $RESOURCE_GROUP \
    --location $LOCATION \

Cree un entorno con un área de trabajo de Log Analytics generada automáticamente.

az containerapp env create \
    --name $ENVIRONMENT \
    --resource-group $RESOURCE_GROUP \
    --location $LOCATION

Muestra el dominio predeterminado del entorno de la aplicación contenedora. Anote este dominio para usarlo en secciones posteriores.

az containerapp env show \
    --name $ENVIRONMENT \
    --resource-group $RESOURCE_GROUP \
    --query properties.defaultDomain

Preparar la aplicación para la implementación

Al implementar la aplicación en Azure Container Apps, la dirección URL de redireccionamiento cambia a la dirección URL de redireccionamiento de la instancia de la aplicación implementada en Azure Container Apps. Siga estos pasos para cambiar esta configuración en el archivo application.yml:

  1. Acceda al archivo src\main\resources\application.yml de la aplicación y cambie el valor de post-logout-redirect-uri por el nombre de dominio de la aplicación implementada, tal como se muestra en el ejemplo siguiente. No olvide reemplazar <API_NAME> y <default-domain-of-container-app-environment> por los valores reales. Por ejemplo, con el dominio predeterminado para el entorno de Azure Container App del paso anterior y ms-identity-api para el nombre de la aplicación, usaría https://ms-identity-api.<default-domain> para el valor post-logout-redirect-uri.

    post-logout-redirect-uri: https://<API_NAME>.<default-domain-of-container-app-environment>
    
  2. Después de guardar este archivo, use el siguiente comando para volver a compilar la aplicación:

    mvn clean package
    

Importante

El archivo application.yml de la aplicación incluye actualmente el valor del secreto de cliente en el parámetro client-secret. No es recomendable dejar este valor en este archivo. También podrías correr un riesgo si haces commit del archivo en un repositorio de Git. Para obtener el enfoque recomendado, consulte Administración de secretos en Azure Container Apps.

Actualizar el registro de la aplicación de Microsoft Entra ID

Como el URI de redirección pasa a ser el de la aplicación implementada en Azure Container Apps, también debe cambiar el URI de redirección en el registro de la aplicación en Microsoft Entra ID. Para realizar este cambio, siga estos pasos:

  1. Vaya a la página de Registros de aplicaciones de la plataforma de identidad de Microsoft para desarrolladores.

  2. Use el recuadro de búsqueda para buscar el registro de la aplicación; por ejemplo, java-servlet-webapp-authentication.

  3. Para abrir el registro de la aplicación, seleccione el nombre.

  4. Seleccione Autenticar desde el menú.

  5. En la sección Web - URIs de redireccionamiento, seleccione Agregar URI.

  6. Rellene el URI de la aplicación, anexando /login/oauth2/code/; por ejemplo, https://<containerapp-name>.<default domain of container app environment>/login/oauth2/code/.

  7. Seleccione Guardar.

Implementar la aplicación

Implemente el paquete JAR en Azure Container Apps.

Nota:

Si fuera necesario, especifique la versión de JDK en las variables del entorno de compilación Java. Para obtener más información, consulte Compilación de variables de entorno para Java en Azure Container Apps.

Ahora puede implementar el archivo WAR con el comando az containerapp up de la CLI.

az containerapp up \
    --name $API_NAME \
    --resource-group $RESOURCE_GROUP \
    --location $LOCATION \
    --environment $ENVIRONMENT \
    --artifact <JAR_FILE_PATH_AND_NAME> \
    --ingress external \
    --target-port 8080 \
    --query properties.configuration.ingress.fqdn

Nota:

La versión predeterminada de JDK es 17. Si necesita cambiar la versión de JDK por problemas de compatibilidad con la aplicación, puede usar el argumento --build-env-vars BP_JVM_VERSION=<YOUR_JDK_VERSION> para ajustar el número de versión.

Para ver más variables de entorno de compilación, consulte Compilación de variables de entorno para Java en Azure Container Apps.

Validación de la aplicación

En este ejemplo, el comando containerapp up incluye el argumento --query properties.configuration.ingress.fqdn, que devuelve el nombre de dominio completo (FQDN), también conocido como la dirección URL de la aplicación. Siga estos pasos para comprobar los registros de la aplicación para investigar cualquier problema de implementación:

  1. Acceda a la dirección URL de la aplicación final en la página Salida de la sección Implementación.

  2. En el panel de navegación de la página Información general de la instancia de Azure Container Apps, seleccione Registros para comprobar los registros de la aplicación.

Explora el ejemplo

Siga estos pasos para explorar el ejemplo:

  1. Observe que el estado de inicio de sesión o de cierre de sesión se muestra en el centro de la pantalla.
  2. Seleccione el botón contextual en la esquina. Este botón indica Iniciar sesión cuando se ejecuta por primera vez la aplicación. Como alternativa, seleccione los detalles del token, solo administradores o usuarios normales. Dado que estas páginas están protegidas y requieren autenticación, se le redirigirá automáticamente a la página de inicio de sesión.
  3. En la página siguiente, siga las instrucciones e inicie sesión con una cuenta en el tenant de Microsoft Entra ID.
  4. En la pantalla de consentimiento, observe los ámbitos que se solicitan.
  5. Tras completar correctamente el flujo de inicio de sesión, debe redirigirse a la página principal (que muestra el estado de inicio de sesión) o a una de las otras páginas, en función del botón que desencadene el flujo de inicio de sesión.
  6. Observe que el botón contextual ahora indica Cerrar sesión y muestra el nombre de usuario.
  7. Si está en la página principal, seleccione Detalles del token de ID para ver algunas de las declaraciones decodificadas del token de ID, incluidos los roles.
  8. Seleccione Solo administradores para ver el /admin_only. Solo los usuarios con el rol de aplicación PrivilegedAdmin pueden ver esta página. De lo contrario, se muestra un mensaje de error de autorización.
  9. Seleccione Usuarios normales para ver la página /regular_user. Solo los usuarios con el rol de aplicación RegularUser o PrivilegedAdmin pueden ver esta página. De lo contrario, se muestra un mensaje de error de autorización.
  10. Use el botón de la esquina para cerrar la sesión. La página de estado refleja el nuevo estado.

Sobre el código

Este ejemplo muestra cómo usar la biblioteca cliente Microsoft Entra ID Spring Boot Starter para Java para permitir que los usuarios inicien sesión en su tenant de Microsoft Entra ID. El ejemplo también usa los iniciadores de spring Oauth2 Client y Spring Web Boot. En el ejemplo se usan notificaciones del token de identificador obtenido de Microsoft Entra ID para mostrar los detalles del usuario que ha iniciado sesión y para restringir el acceso a algunas páginas mediante la notificación de roles para la autorización.

Contenido

En la tabla siguiente se muestra el contenido de la carpeta del proyecto de ejemplo:

Archivo/carpeta Descripción
pom.xml Dependencias de aplicaciones.
src/main/resources/templates/ Plantillas de Thymeleaf para la interfaz de usuario.
src/main/resources/application.yml Configuración de la biblioteca de inicio de arranque de inicio de Microsoft Entra ID y aplicación.
src/main/java/com/microsoft/azuresamples/msal4j/msidentityspringbootwebapp/ Este directorio contiene el punto de entrada, el controlador y las clases de configuración principales de la aplicación.
.../MsIdentitySpringBootWebappApplication.java Clase Main.
.../SampleController.java Controlador con asignaciones de puntos de conexión.
.../SecurityConfig.java Configuración de seguridad: por ejemplo, qué rutas requieren autenticación.
.../Utilities.java Clase de utilidad - por ejemplo, para filtrar claims del token de ID.
CHANGELOG.md Lista de cambios en la muestra.
CONTRIBUTING.md Directrices para contribuir al ejemplo.
LICENCIAS La licencia de la muestra.

Notificaciones de token de ID

Para extraer los detalles del token, la aplicación usa el objeto AuthenticationPrincipal y OidcUser de Spring Security en una asignación de solicitudes, como se muestra en el ejemplo siguiente. Consulta el Controlador de ejemplo para conocer todos los detalles sobre cómo esta aplicación usa las declaraciones del token de ID.

import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
//...
@GetMapping(path = "/some_path")
public String tokenDetails(@AuthenticationPrincipal OidcUser principal) {
    Map<String, Object> claims = principal.getIdToken().getClaims();
}

Procesar una declaración de roles en el token de ID

La declaración de roles del token incluye los nombres de los roles que tiene asignados el usuario que ha iniciado sesión, como se muestra en el ejemplo siguiente:

{
  ...
  "roles": [
    "PrivilegedAdmin",
    "RegularUser",]
  ...
}

Una forma habitual de acceder a los nombres de los roles se describe en la sección Declaraciones del token de identificador.

Microsoft Entra ID Boot Starter v3.3 y versiones posteriores también analiza automáticamente la notificación de roles y agrega cada rol a Authorities del usuario que ha iniciado sesión, añadiendo el prefijo de cadena APPROLE_. Esta configuración permite a los desarrolladores usar roles de aplicación con anotaciones de condición PrePost de Spring mediante el método hasAuthority. Por ejemplo, puede encontrar las siguientes condiciones @PreAuthorize que se muestran en SampleController.java:

@GetMapping(path = "/admin_only")
@PreAuthorize("hasAuthority('APPROLE_PrivilegedAdmin')")
public String adminOnly(Model model) {
    // restrict to users who have PrivilegedAdmin app role only
}
@GetMapping(path = "/regular_user")
@PreAuthorize("hasAnyAuthority('APPROLE_PrivilegedAdmin','APPROLE_RegularUser')")
public String regularUser(Model model) {
    // restrict to users who have any of RegularUser or PrivilegedAdmin app roles
}

El código siguiente obtiene una lista completa de autoridades para un usuario determinado:

@GetMapping(path = "/some_path")
public String tokenDetails(@AuthenticationPrincipal OidcUser principal) {
   Collection<? extends GrantedAuthority> authorities = principal.getAuthorities();
}

Para el inicio de sesión, la aplicación realiza una solicitud al punto de conexión de inicio de sesión de Microsoft Entra ID configurado automáticamente por la biblioteca cliente de Spring Boot Starter de Microsoft Entra ID para Java, como se muestra en el ejemplo siguiente:

<a class="btn btn-success" href="/oauth2/authorization/azure">Sign In</a>

Para cerrar sesión, la aplicación realiza una solicitud POST al punto de conexión logout, como se muestra en el ejemplo siguiente:

<form action="#" th:action="@{/logout}" method="post">
  <input class="btn btn-warning" type="submit" value="Sign Out" />
</form>

Elementos de la interfaz de usuario dependientes de la autenticación

La aplicación tiene cierta lógica sencilla en las páginas de plantilla de la interfaz de usuario para determinar el contenido que se va a mostrar en función de si el usuario está autenticado, como se muestra en el ejemplo siguiente mediante etiquetas Spring Security Thymeleaf:

<div sec:authorize="isAuthenticated()">
  this content only shows to authenticated users
</div>
<div sec:authorize="isAnonymous()">
  this content only shows to not-authenticated users
</div>

Protege las rutas con AADWebSecurityConfigurerAdapter

De forma predeterminada, la aplicación protege las páginas Detalles del token de identificador, Solo administradores y Usuarios normales para que solo los usuarios que hayan iniciado sesión puedan acceder a ellas. La aplicación configura estas rutas mediante la propiedad app.protect.authenticated del archivo application.yml. Para configurar los requisitos específicos de su aplicación, puede extender AADWebSecurityConfigurationAdapter en una de sus clases. Para obtener un ejemplo, consulte la clase SecurityConfig de esta aplicación, que se muestra en el código siguiente:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends AADWebSecurityConfigurerAdapter{
  @Value( "${app.protect.authenticated}" )
  private String[] protectedRoutes;

    @Override
    public void configure(HttpSecurity http) throws Exception {
    // use required configuration form AADWebSecurityAdapter.configure:
    super.configure(http);
    // add custom configuration:
    http.authorizeRequests()
      .antMatchers(protectedRoutes).authenticated()     // limit these pages to authenticated users (default: /token_details, /admin_only, /regular_user)
      .antMatchers("/**").permitAll();                  // allow all other routes.
    }
}

Más información

Para obtener más información sobre cómo funcionan los protocolos de OAuth 2.0 en este escenario y otros escenarios, vea Escenarios de autenticación para Microsoft Entra ID.