Keycloak: Autenticación con Java Quarkus

Implementación de servicio de autenticación Keycloak con api rest en Java Quarkus

Las últimas versiones de Keycloak ya no utilizan WildFly como servidor y motor de distribución, ahora implementan un servidor de aplicaciones basado en Java Quarkus. Por tanto, hay algunos cambios en la instalación y configuración del servicio.

El siguiente archivo manifiesto contiene la especificación básica de servicio Keycloak. Define usuario administrador, un volumen en directorio local para persistencia (base de datos H2), puerto tcp y un comando de modo de operación. El modo de operación de desarrollo (comando 'start-dev') es útil en entornos de prueba y desarrollo porque permite http y es menos exigente en cuanto a requisitos de seguridad y resolución de nombres de hosts. Para entornos de produción se debe utilizar el modo de operación 'start'.

docker-compose.yml

version: '3'

services:
  keycloak:
    image: quay.io/keycloak/keycloak:18.0.2
    container_name: keycloak
    restart: unless-stopped
    environment:
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin123
    volumes:
      - ./data:/opt/keycloak/data/
    ports:
      - 8080:8080
    command:
      - start-dev

Vamos a crear un realm de ejemplo ('corp'), junto a un cliente ('service'), usuarios ('lab', 'dev') y roles ('user', 'admin'). Esto lo haremos en el terminal, utilizando el servicio admin-cli disponible en el directorio /opt/keycloak/bin/ del contenedor.

Iniciar servicio Keycloak e ingresar a contenedor:

docker-compose run -d

docker exec -it keycloak bash

Acceder a directorio de binarios de administración de Keycloak y autenticarse con usuario 'admin' definido en docker compose:

cd opt/keycloak/bin/

./kcadm.sh config credentials --server http://localhost:8080 --realm master --user admin

Crear realm:

./kcadm.sh create realms -s realm=corp -s enabled=true -o

Crear cliente, asociarlo al nuevo realm, definir parámetros de acceso y url de servicio rest:

./kcadm.sh create clients -r corp -s clientId=service -s enabled=true 
-s clientAuthenticatorType=client-secret -s secret=qwe1234. 
-s publicClient=false -s bearerOnly=false -s standardFlowEnabled=true 
-s directAccessGrantsEnabled=true -s serviceAccountsEnabled=true 
-s "redirectUris=[\"http://localhost:8000/api/*\"]"

Crear usuarios y asociarlos al nuevo realm

./kcadm.sh create users -r corp -s username=lab -s enabled=true
./kcadm.sh set-password -r corp --username lab --new-password lab123

./kcadm.sh create users -r corp -s username=dev -s enabled=true
./kcadm.sh set-password -r corp --username dev --new-password dev123

Crear roles y asociarlos al realm y a los nuevos usuarios


./kcadm.sh create roles -r corp -s name=user
./kcadm.sh create roles -r corp -s name=admin

./kcadm.sh add-roles --uusername lab --rolename user -r corp

./kcadm.sh add-roles --uusername dev --rolename admin -r corp

Servicio Rest Java Quarkus

Dependencias en archivo pom.xml de proyecto Java Quarkus de ejemplo:

  <dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-keycloak-authorization</artifactId>
  </dependency>
  <dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-resteasy-reactive-jackson</artifactId>
  </dependency>
  <dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-oidc</artifactId>
  </dependency>

Archivo de propiedades de proyecto java, se define la url de servicio keycloak con el realm de ejemplo 'corp' y cliente 'service':

quarkus.http.port=8000

quarkus.oidc.auth-server-url=http://localhost:8080/realms/corp
quarkus.oidc.client-id=service

Clases con métodos para pruebas básicas de acceso a recursos protegidos utilizando la anotación @RolesAllowed:

AdminResource.java:

@Path("/api/admin")
public class AdminResource {

    @GET
    @Path("/info")
    @RolesAllowed("admin")
    @Produces(MediaType.TEXT_PLAIN)
    public String user() {
        return "admin resource granted..";
    }

}

UserResouce.java, tiene un recurso público de ejemplo ('/api/users/open'):

@Path("/api/users")
public class UserResource {

    @GET
    @Path("/info")
    @RolesAllowed("user")
    @Produces(MediaType.TEXT_PLAIN)
    public String user() {
        return "user resource granted..";
    }

    @GET
    @Path("/open")
    @PermitAll
    @Produces(MediaType.TEXT_PLAIN)
    public String open() {
        return "public resource..";
    }
}

Pruebas con curl: autenticarse y obtener access token:

curl -X POST http://localhost:8080/realms/corp/protocol/openid-connect/token \
    --user service:qwe1234. \
    -H 'content-type: application/x-www-form-urlencoded' \
    -d 'username=lab&password=lab123&grant_type=password'


curl -X POST http://localhost:8080/realms/corp/protocol/openid-connect/token \
    --user service:qwe1234. \
    -H 'content-type: application/x-www-form-urlencoded' \
    -d 'username=dev&password=dev123&grant_type=password'

Probar servicio java quarkus con curl, utilizando access token:

curl -v -X GET \
   http://localhost:8000/api/users/info \
   -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia.."


curl -v -X GET \
   http://localhost:8000/api/admin/info \
   -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2l.."


Puedes descargar el código fuente completo de este ejemplo en el siguiente enlace.


¿Tienes alguna consulta?
Puedes contactarme enviándome un mensaje desde aquí.