fix: Affichage dynamique utilisateur et déconnexion Keycloak
- UserSessionBean: Getters dynamiques pour nom, email, rôle - Suppression @PostConstruct pour récupération temps réel - Déconnexion: Redirection directe vers Keycloak logout endpoint - Topbar: Bouton logout avec p:commandButton ajax=false 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -24,8 +24,6 @@ import java.io.Serializable;
|
|||||||
*/
|
*/
|
||||||
@Named("userSession")
|
@Named("userSession")
|
||||||
@SessionScoped
|
@SessionScoped
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class UserSessionBean implements Serializable {
|
public class UserSessionBean implements Serializable {
|
||||||
|
|
||||||
@@ -38,75 +36,80 @@ public class UserSessionBean implements Serializable {
|
|||||||
@IdToken
|
@IdToken
|
||||||
JsonWebToken idToken;
|
JsonWebToken idToken;
|
||||||
|
|
||||||
private String nomComplet;
|
|
||||||
private String email;
|
|
||||||
private String avatarUrl;
|
|
||||||
private String role;
|
|
||||||
private int nombreNotificationsNonLues;
|
|
||||||
private int nombreMessagesNonLus;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise les données de l'utilisateur connecté depuis le token OIDC.
|
* Récupère le nom complet de l'utilisateur depuis le token OIDC.
|
||||||
|
* Méthode dynamique qui récupère les informations à chaque appel.
|
||||||
*/
|
*/
|
||||||
@PostConstruct
|
public String getNomComplet() {
|
||||||
public void init() {
|
|
||||||
try {
|
try {
|
||||||
// Récupération depuis le token OIDC/JWT
|
if (securityIdentity != null && securityIdentity.getPrincipal() != null && idToken != null) {
|
||||||
if (securityIdentity != null && securityIdentity.getPrincipal() != null) {
|
|
||||||
log.info("Initialisation des informations utilisateur depuis OIDC");
|
|
||||||
|
|
||||||
// Nom complet (preferred_username ou name)
|
// Nom complet (preferred_username ou name)
|
||||||
nomComplet = idToken.getClaim("name");
|
String nom = idToken.getClaim("name");
|
||||||
if (nomComplet == null || nomComplet.trim().isEmpty()) {
|
if (nom == null || nom.trim().isEmpty()) {
|
||||||
nomComplet = idToken.getClaim("preferred_username");
|
nom = idToken.getClaim("preferred_username");
|
||||||
}
|
}
|
||||||
if (nomComplet == null || nomComplet.trim().isEmpty()) {
|
if (nom == null || nom.trim().isEmpty()) {
|
||||||
nomComplet = securityIdentity.getPrincipal().getName();
|
nom = securityIdentity.getPrincipal().getName();
|
||||||
}
|
}
|
||||||
|
return nom != null ? nom : "Utilisateur";
|
||||||
// Email
|
|
||||||
email = idToken.getClaim("email");
|
|
||||||
|
|
||||||
// Avatar par défaut
|
|
||||||
avatarUrl = "/resources/freya-layout/images/avatar-profilemenu.png";
|
|
||||||
|
|
||||||
// Rôles (premier rôle trouvé)
|
|
||||||
if (securityIdentity.getRoles() != null && !securityIdentity.getRoles().isEmpty()) {
|
|
||||||
role = securityIdentity.getRoles().iterator().next();
|
|
||||||
// Formatage du rôle pour affichage (enlever préfixes)
|
|
||||||
role = role.replace("_", " ").replace("-", " ");
|
|
||||||
role = capitalizeWords(role);
|
|
||||||
} else {
|
|
||||||
role = "Utilisateur";
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("Utilisateur connecté: {} ({})", nomComplet, email);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log.warn("SecurityIdentity non disponible, utilisation des valeurs par défaut");
|
|
||||||
setDefaultValues();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Récupérer le nombre réel de notifications et messages depuis l'API
|
|
||||||
nombreNotificationsNonLues = 0;
|
|
||||||
nombreMessagesNonLus = 0;
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Erreur lors de l'initialisation du UserSession", e);
|
log.error("Erreur lors de la récupération du nom complet", e);
|
||||||
setDefaultValues();
|
|
||||||
}
|
}
|
||||||
|
return "Utilisateur";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Valeurs par défaut si OIDC non disponible.
|
* Récupère l'email de l'utilisateur depuis le token OIDC.
|
||||||
*/
|
*/
|
||||||
private void setDefaultValues() {
|
public String getEmail() {
|
||||||
nomComplet = "Utilisateur";
|
try {
|
||||||
email = "utilisateur@btpxpress.com";
|
if (securityIdentity != null && securityIdentity.getPrincipal() != null && idToken != null) {
|
||||||
avatarUrl = "/resources/freya-layout/images/avatar-profilemenu.png";
|
String email = idToken.getClaim("email");
|
||||||
role = "Utilisateur";
|
return email != null ? email : "utilisateur@btpxpress.com";
|
||||||
nombreNotificationsNonLues = 0;
|
}
|
||||||
nombreMessagesNonLus = 0;
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la récupération de l'email", e);
|
||||||
|
}
|
||||||
|
return "utilisateur@btpxpress.com";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retourne l'URL de l'avatar (par défaut).
|
||||||
|
*/
|
||||||
|
public String getAvatarUrl() {
|
||||||
|
return "/resources/freya-layout/images/avatar-profilemenu.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère le rôle de l'utilisateur depuis SecurityIdentity.
|
||||||
|
*/
|
||||||
|
public String getRole() {
|
||||||
|
try {
|
||||||
|
if (securityIdentity != null && securityIdentity.getRoles() != null && !securityIdentity.getRoles().isEmpty()) {
|
||||||
|
String role = securityIdentity.getRoles().iterator().next();
|
||||||
|
// Formatage du rôle pour affichage (enlever préfixes)
|
||||||
|
role = role.replace("_", " ").replace("-", " ");
|
||||||
|
return capitalizeWords(role);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la récupération du rôle", e);
|
||||||
|
}
|
||||||
|
return "Utilisateur";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nombre de notifications non lues (TODO: implémenter via API).
|
||||||
|
*/
|
||||||
|
public int getNombreNotificationsNonLues() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nombre de messages non lus (TODO: implémenter via API).
|
||||||
|
*/
|
||||||
|
public int getNombreMessagesNonLus() {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -130,17 +133,18 @@ public class UserSessionBean implements Serializable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retourne les initiales de l'utilisateur pour l'avatar.
|
* Retourne les initiales de l'utilisateur pour l'avatar.
|
||||||
*
|
*
|
||||||
* @return Les initiales (ex: "JD" pour "Jean Dupont")
|
* @return Les initiales (ex: "JD" pour "Jean Dupont")
|
||||||
*/
|
*/
|
||||||
public String getInitiales() {
|
public String getInitiales() {
|
||||||
|
String nomComplet = getNomComplet();
|
||||||
if (nomComplet == null || nomComplet.trim().isEmpty()) {
|
if (nomComplet == null || nomComplet.trim().isEmpty()) {
|
||||||
return "U";
|
return "U";
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] parts = nomComplet.trim().split("\\s+");
|
String[] parts = nomComplet.trim().split("\\s+");
|
||||||
if (parts.length >= 2) {
|
if (parts.length >= 2) {
|
||||||
return String.valueOf(parts[0].charAt(0)).toUpperCase() +
|
return String.valueOf(parts[0].charAt(0)).toUpperCase() +
|
||||||
String.valueOf(parts[1].charAt(0)).toUpperCase();
|
String.valueOf(parts[1].charAt(0)).toUpperCase();
|
||||||
} else if (parts.length == 1) {
|
} else if (parts.length == 1) {
|
||||||
return parts[0].substring(0, Math.min(2, parts[0].length())).toUpperCase();
|
return parts[0].substring(0, Math.min(2, parts[0].length())).toUpperCase();
|
||||||
@@ -150,21 +154,44 @@ public class UserSessionBean implements Serializable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Action de déconnexion OIDC/Keycloak.
|
* Action de déconnexion OIDC/Keycloak.
|
||||||
|
* Redirige vers l'endpoint de logout Keycloak pour détruire la session.
|
||||||
*
|
*
|
||||||
* @return Null pour déclencher une redirection externe
|
* @return Null pour déclencher une redirection externe
|
||||||
*/
|
*/
|
||||||
public String deconnecter() {
|
public String deconnecter() {
|
||||||
try {
|
try {
|
||||||
log.info("Déconnexion de l'utilisateur: {}", nomComplet);
|
log.info("Déconnexion de l'utilisateur: {}", getNomComplet());
|
||||||
// La déconnexion OIDC est gérée automatiquement par Quarkus
|
|
||||||
// via le endpoint /q/oidc/logout
|
|
||||||
// Redirection vers le logout endpoint
|
|
||||||
jakarta.faces.context.FacesContext facesContext = jakarta.faces.context.FacesContext.getCurrentInstance();
|
jakarta.faces.context.FacesContext facesContext = jakarta.faces.context.FacesContext.getCurrentInstance();
|
||||||
jakarta.faces.context.ExternalContext externalContext = facesContext.getExternalContext();
|
jakarta.faces.context.ExternalContext externalContext = facesContext.getExternalContext();
|
||||||
|
|
||||||
// Rediriger vers l'endpoint de logout OIDC de Quarkus
|
// Construction de l'URL de logout Keycloak
|
||||||
String logoutUrl = externalContext.getRequestContextPath() + "/q/oidc/logout";
|
String keycloakLogoutUrl = "https://security.lions.dev/realms/btpxpress/protocol/openid-connect/logout";
|
||||||
externalContext.redirect(logoutUrl);
|
|
||||||
|
// URL de redirection après logout
|
||||||
|
String baseUrl = externalContext.getRequestScheme() + "://" +
|
||||||
|
externalContext.getRequestServerName() + ":" +
|
||||||
|
externalContext.getRequestServerPort() +
|
||||||
|
externalContext.getRequestContextPath();
|
||||||
|
|
||||||
|
String postLogoutRedirectUri = baseUrl + "/";
|
||||||
|
|
||||||
|
// Construire l'URL complète avec les paramètres
|
||||||
|
StringBuilder logoutUrl = new StringBuilder(keycloakLogoutUrl);
|
||||||
|
logoutUrl.append("?post_logout_redirect_uri=").append(java.net.URLEncoder.encode(postLogoutRedirectUri, "UTF-8"));
|
||||||
|
|
||||||
|
// Ajouter le id_token_hint si disponible
|
||||||
|
if (idToken != null && idToken.getRawToken() != null) {
|
||||||
|
logoutUrl.append("&id_token_hint=").append(java.net.URLEncoder.encode(idToken.getRawToken(), "UTF-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Redirection vers Keycloak logout: {}", keycloakLogoutUrl);
|
||||||
|
|
||||||
|
// Invalider la session HTTP locale
|
||||||
|
externalContext.invalidateSession();
|
||||||
|
|
||||||
|
// Rediriger vers Keycloak logout
|
||||||
|
externalContext.redirect(logoutUrl.toString());
|
||||||
facesContext.responseComplete();
|
facesContext.responseComplete();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -107,10 +107,11 @@
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<h:form>
|
<h:form>
|
||||||
<h:commandLink action="#{userSession.deconnecter()}" styleClass="logout-link">
|
<p:commandButton action="#{userSession.deconnecter()}"
|
||||||
<i class="pi pi-sign-out"></i>
|
value="Logout"
|
||||||
<span>Logout</span>
|
styleClass="logout-link p-button-text"
|
||||||
</h:commandLink>
|
ajax="false"
|
||||||
|
icon="pi pi-sign-out"/>
|
||||||
</h:form>
|
</h:form>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
Reference in New Issue
Block a user