So fügen Sie eine zusätzliche Seite für Zustimmungen des Benutzer während des Anmeldevorgangs hinzu
In den meisten täglich genutzten Anwendungen werden Sie während des Anmeldevorgangs aufgefordert, der Weitergabe Ihrer Daten oder den Nutzungsbedingungen einer Endbenutzer-Lizenzvereinbarung zuzustimmen. Diese Art von Aufforderungen zur Zustimmung informieren die Benutzer über die Nutzungsbeschränkungen der Anwendung oder geben an, inwieweit der Softwareanbieter für die Nutzung der Anwendung durch die Benutzer haftet. Vor allem in Anbetracht der jüngsten Datenschutzbestimmungen wie der DSGVO und verschiedener Medienberichte über die Verletzung personenbezogener Daten durch bekannte Softwareanbieter ist es wichtig, die Zustimmung des Benutzers zur Weitergabe von Daten einzuholen. In der Regel wird der Anmeldevorgang abgebrochen, wenn die Benutzer nicht zustimmen.
Die Anwendungen fragen entweder während der Anmeldung des Benutzers, der Registrierung des Benutzers oder bei der Installation der Anwendung nach der Zustimmung. Es gibt keine feste Regel, wann diese zusätzlichen Seiten angezeigt werden müssen. Bei einigen Anwendungen werden die Nutzungsbedingungen nicht auf einer separaten Seite angezeigt, sondern in einem Kontrollkästchen auf der Anmelde- oder Registrierungsseite selbst.
Dieser Blogbeitrag bezieht sich auf Fälle, in denen Sie während des Anmeldevorgangs verschiedene Zustimmungen vom Endbenutzer einholen möchten.
Fallbeispiel: Hinzufügen einer zusätzlichen Genehmigungsseite während des Anmeldevorgangs mit dem WSO2 Identity Server
Sie müssen eine zusätzliche Seite einblenden, um die Zustimmung des Benutzers, die Zustimmung zu den Nutzungsbedingungen oder einen Haftungsausschluss einzuholen, bevor der Benutzer sich bei der Verbraucheranwendung anmelden kann.
Hier wird dieser zusätzliche Schritt als Teil des Anmeldevorgangs ausgeführt. In solchen Fällen muss der Identitätsanbieter, der für die Authentifizierung/Anmeldung bei der Anwendung zuständig ist, diese Aufgabe übernehmen. Wenn die Anwendung keinen Identitätsanbieter für die Anmeldung nutzt, ist es Sache der Anwendungsentwickler, diese zusätzliche Lösung und die Zustimmungsseiten zu pflegen.
Heute sehen wir uns an, wie der WSO2 Identity Server – der als Identitätsanbieter für Anwendungen von Drittanbietern fungiert – Ihnen dabei helfen kann, eine solche zusätzliche Genehmigungsseite ganz einfach in den Anmeldevorgang einzufügen.
Lösung: Verwenden von Handlern nach der Authentifizierung im WSO2 Identity Server zur Handhabung zusätzlicher Genehmigungsseiten während des Anmeldevorgangs
Für solche Fälle stellt der WSO2 Identity Server einen Erweiterungspunkt namens „Post Authentication Handler“ bereit.
Verwechseln Sie dies nicht mit dem „Event Handler“-Erweiterungspunkt des WSO2 Identity Server.
Im Ereignis-Framework des WSO2 Identity Server gibt es Ereignisse, die vor der Authentifizierung und nach der Authentifizierung ausgelöst werden, die jeweils „PRE_AUTHENTICATION“ und „POST_AUTHENTICATION“ genannt werden. Sie können diese Ereignisse abonnieren und Handler schreiben, um benutzerdefinierte Funktionen auszulösen oder auszuführen.
„Post Authentication Handler“ sind allerdings nicht in derselben Kategorie. Sie werden ausgelöst, sobald alle Authentifizierungsschritte abgeschlossen sind.
Wenn Sie mehrere Authentifizierungsschritte konfigurieren, wie z. B. die Authentifizierung per Benutzername/Passwort + SMS OTP, oder wenn Sie einen föderierten Authentifikator wie die Facebook/Google-Authentifizierung für Ihre Anwendung konfigurieren, werden die Post Authentication Handler erst ausgeführt, wenn alle Authentifizierungsschritte erfolgreich abgeschlossen sind.
3 Schritte zum Erstellen eines Post Authentication Handlers
Angenommen, Ihre Anwendung nutzt den WSO2 Identity Server für die Benutzerauthentifizierung. Vor kurzem wurde gefordert, eine neue Seite mit den Nutzungsbedingungen hinzuzufügen, um die Zustimmung der Benutzer einzuholen. Es ist zwingend erforderlich, dass Ihre Anwendung für den Zugriff auf Ihre Anwendung die Zustimmung der Benutzer zu den Nutzungsbedingungen einholt, bevor der Anmeldevorgang abgeschlossen werden kann. Im Folgenden finden Sie die wichtigsten Schritte, mit denen Sie diese Anforderung mit Hilfe des WSO2 Identitätsservers erfüllen können.
- Erstellen Sie eine jsp-Datei mit der Seite, die die Benutzer sehen und zu der sie ihre Zustimmung geben sollen. Beispiel: terms-and-conditions.jsp. Speichern Sie es im Ordner WSO2-IS/repository/deployment/server/webapps/authenticationendpoint.
- Schreiben Sie die Java-Komponente für den Post Authentication Handler (OSGi-Bundle).
- Erstellen Sie ein Maven-Projekt.
- Als Hauptabhängigkeit in diesem Projekt müssen Sie „org. wso2.carbon.identity.application. authentication.framework“ verwenden. Fügen Sie die korrekten Versionen des Authentifizierungs-Frameworks hinzu, die zu der von Ihnen verwendeten WSO2 IS-Distribution passen.
- Erstellen Sie eine neue Java-Klasse, die den „AbstractPostAuthnHandler“ erweitert.
- Beispiel: öffentliche Klasse TermsAndConditionsPostAuthenticationHandler erweitert AbstractPostAuthnHandler {
- Überschreiben Sie nun die Methode „handle“ in der neuen Klasse und implementieren Sie die Funktion, um die neue Seite mit den Nutzungsbedingungen aufzurufen und die Anmeldung bei der Anwendung zuzulassen oder abzulehnen, je nachdem, ob der Benutzer zugestimmt hat oder nicht.
@Override public PostAuthnHandlerFlowStatus handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationContext authenticationContext) throws PostAuthenticationFailedException { |
- Der Rückgabetyp ( PostAuthnHandlerFlowStatus ) dieser Methode ist ein Enum, das aus Werten besteht
SUCCESS_COMPLETED – Gibt an, dass die Aufgaben des Post Authentication Handlers erfolgreich ausgeführt wurden.
INCOMPLETE – Zeigt an, dass die Aufgaben des Post Authentication Handlers noch nicht abgeschlossen sind. Beispiel: Die Seite mit den Nutzungsbedingungen wird dem Benutzer noch nicht angezeigt.
UNSUCCESS_COMPLETED – Zeigt an, dass die Aufgabe des Post Authentication Handlers abgeschlossen wurde, die Ausführung jedoch nicht erfolgreich war. Beispiel: Der authentifizierte Benutzer ist „null“. Überspringen Sie daher die Ausführung des Post Authentication Handlers, indem Sie diesen Wert senden.
- Innerhalb der „Handle“-Methode muss folgende Funktion implementiert werden.
- Überprüfen Sie, dass der authentifizierte Benutzer nicht null ist.
- Überprüfen Sie, ob der Benutzer auf die beabsichtigte Seite weitergeleitet wird. Beispiel: terms-and-conditions.jsp
- Wenn sich der Benutzer auf der neu hinzugefügten Seite befindet, überprüfen Sie, ob die Genehmigung erteilt wurde oder nicht. Wenn keine Genehmigung erteilt wird, lösen Sie eine Ausnahme aus.
- Wenn sich der Benutzer immer noch nicht auf der gewünschten Seite befindet, leiten Sie ihn über eine http-Umleitung auf die gewünschte Seite um. Beispiel: httpServletResponse.sendRedirect()
- Registrieren Sie nun die Implementierungsklasse Ihres Handlers als PostAuthenticationHandler-OSGi-Dienst. Beispiel: context.getBundleContext().registerService( PostAuthenticationHandler .class.getName(), TermsAndConditionsPostAuthenticationHandler , null);
- Konfigurieren Sie den neuen Handler in der Datei „deployment.toml“.
Bijvoorbeeld:
[[event_listener]] id = „custom_post_auth_listener“ type = „org.wso2.carbon.identity.core.handler.AbstractIdentityHandler“ name = „org.wso2.carbon.identity.post.authn.handler.custom. TermsAndConditionsPostAuthenticationHandler “ order = 100 |
Die Handler werden zuerst mit der niedrigsten Nummer ausgeführt. Hier habe ich 100 eingetragen. Mein Handler wird also ausgeführt, nachdem alle anderen Post Authentication Handler (wie die Standardseite zur Zustimmungsverwaltung) abgeschlossen sind.
Der High-Level-Flow während der Anmeldung sieht wie folgt aus:
Verwendung von extern gehosteten Seiten: Weiterleitung zu externen Zustimmungs-Seiten von der Implementierung der Post Authentication Handler-Klasse des WSO2 Identity Server
Wie Sie im oben gezeigten Flussdiagramm gesehen haben, leiten wir innerhalb der Implementierung der Handler-Klasse mit Hilfe der sendRedirect Methode auf die gewünschte Seite um. Anstatt also Ihre Seiten innerhalb der Webapplikation authenticationendpoint zu hosten, können Sie die gewünschten Seiten für Zustimmung/Haftungsausschluss/Nutzungsbedingungen extern hosten und von der Handler-Implementierungsklasse auf die extern oder separat gehostete Seite umleiten.
Zu beachtende Punkte:
- Stellen Sie sicher, dass Sie den Parameter „sessionDataKey“ als Anfrageparameter bei der Umleitung innerhalb der Implementierungsklasse des Handlers verwenden.
Beispiel: httpServletResponse.sendRedirect(“https://myhost:8080/consent.jsp”+”? sessionDataKey =” + authenticationContext.getContextIdentifier() );
- Stellen Sie dann sicher, dass Sie denselben seesionDataKey als versteckten Eingabeparameter von Ihrer benutzerdefinierten jsp-Datei zurück an den commonauth-Endpunkt des WSO2 Identity Server übergeben.
Beispiel: <input type=“hidden“ name=“<%=“ sessionDataKey „%>“ value=“<%=Encode.forHtmlAttribute(r equest.getParameter(„sessionDataKey“ )) %>“/>
Dieser Parameter hilft dabei, die Korrelation zwischen verschiedenen Umleitungen innerhalb eines Anfrageflusses aufrechtzuerhalten. Da es sich bei HTTP um ein zustandsloses Protokoll handelt, hilft dieser Parameter dem WSO2 Identity Server dabei, den Status eines bestimmten Anfrageflusses zu verfolgen, der an den WSO2 Identity Server gerichtet ist.
5 Standardimplementierungen von Post Authentication Handlern im WSO2 Identity Server
- ConsentMgtPostAuthnHandler : Verarbeitet Benutzerzustimmungen nach erfolgreicher Authentifizierung. Ruft die Seite für die Benutzerzustimmung auf.
- JITProvisioningPostAuthenticationHandler: Erledigt Just-In-Time Provisioning, nachdem ein Benutzer gegenüber einem externen Identitätsanbieter (föderierter IDP) authentifiziert wurde. Fordert Seiten für Just-In-Time Provisioning an.
- PostAuthAssociationHandler: Verantwortlich für die Zuordnung von föderierten Benutzern zu lokalen Benutzerkonten. Bei dieser Implementierung werden keine Seitenumleitungen angefordert. Es werden nur die lokalen Benutzerzuordnungen hinter den Kulissen durchgeführt.
- PostAuthenticatedSubjectIdentifierHandler: Verantwortlich für das Setzen des Subject Identifiers für einen authentifizierten Benutzer.
- PostAuthnMissingClaimHandler: Verantwortlich für das Einholen fehlender Pflichtangaben. Ruft eine Seite auf, die den Benutzer auffordert, die Pflichtangaben auszufüllen, sofern diese noch nicht ausgefüllt sind.
Zusammenfassung
Die Verwendung von Post Authentication Handlern im WSO2 Identity Server zum Anfordern zusätzlicher Seiten während des Anmeldeprozesses kann für eine Vielzahl von Anwendungsfällen verwendet werden, z. B. für die Abfrage von Benutzerzustimmungen, Genehmigungen oder Texteingaben.
Post Authentication Handler können auch verwendet werden, um benutzerdefinierte Funktionen während des Anmeldevorgangs auszuführen, ohne dass eine benutzerdefinierte Seitenumleitung erforderlich ist. Es ist wichtig zu beachten, dass diese Handler nur nach Abschluss der Authentifizierungssequenz/-schritte ausgeführt werden.
Die Standardimplementierung des Produkts bietet zusätzliche Funktionen wie JIT-Provisioning und fehlende Claim Handler. Verwenden Sie den folgenden Beispiel-Quellcodeausschnitt, um Ihren eigenen Post Authentication Handler im WSO2 Identity Server zu implementieren. Weitere Beispiele und Informationen finden Sie in unserem Blogpost!
Wenn Sie weitere Beispiele für die Verwendung des WSO2 Identity Server suchen, sollten Sie sich das offizielle GitHub-Repository ansehen. Ausführliche Anleitungen zur Verwendung dieser Beispiele finden Sie auch in diesem hilfreichen Blogbeitrag von Yenlo – Mit den WSO2 Identity Server-Samples arbeiten. Beide Ressourcen bieten Ihnen wertvolle Einblicke und Codeausschnitte, die Ihnen den Einstieg in Ihre Projekte mit dem WSO2 Identity Server erleichtern.
package org.yenlo.carbon.identity.post.authn.handler.termsandconditions;
import org.wso2.carbon.identity.application.authentication.framework.config.ConfigurationFacade;
import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext;
import org.wso2.carbon.identity.application.authentication.framework.exception.PostAuthenticationFailedException;
import org.wso2.carbon.identity.application.authentication.framework.handler.request.AbstractPostAuthnHandler;
import org.wso2.carbon.identity.application.authentication.framework.handler.request.PostAuthnHandlerFlowStatus;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TermsAndConditionsPostAuthenticationHandler extends AbstractPostAuthnHandler {
private String CONSENT_POPPED_UP = "consentPoppedUp";
@Override
public PostAuthnHandlerFlowStatus handle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
AuthenticationContext authenticationContext)
throws PostAuthenticationFailedException {
if (getAuthenticatedUser(authenticationContext) == null) {
return PostAuthnHandlerFlowStatus.SUCCESS_COMPLETED;
}
if (isConsentPoppedUp(authenticationContext)) {
if (httpServletRequest.getParameter("consent").equalsIgnoreCase("approve")) {
return PostAuthnHandlerFlowStatus.SUCCESS_COMPLETED;
} else {
throw new PostAuthenticationFailedException("Cannot access this application : Consent Denied",
"Consent denied");
}
} else {
try {
httpServletResponse.sendRedirect
(ConfigurationFacade.getInstance().getAuthenticationEndpointURL().replace("/login.do", ""
) + "/termsandconditions" + ".jsp?sessionDataKey=" + authenticationContext.getContextIdentifier() +
"&application=" + authenticationContext
.getSequenceConfig().getApplicationConfig().getApplicationName());
setConsentPoppedUpState(authenticationContext);
return PostAuthnHandlerFlowStatus.INCOMPLETE;
} catch (IOException e) {
throw new PostAuthenticationFailedException("Invalid Consent", "Error while redirecting", e);
}
}
}
@Override
public String getName() {
return "DisclaimerHandler";
}
private AuthenticatedUser getAuthenticatedUser(AuthenticationContext authenticationContext) {
AuthenticatedUser user = authenticationContext.getSequenceConfig().getAuthenticatedUser();
return user;
}
private void setConsentPoppedUpState(AuthenticationContext authenticationContext) {
authenticationContext.addParameter(CONSENT_POPPED_UP, true);
}
private boolean isConsentPoppedUp(AuthenticationContext authenticationContext) {
return authenticationContext.getParameter(CONSENT_POPPED_UP) != null;
}
}
OSGi Service-Komponenten-Klasse:
package org.yenlo.carbon.identity.post.authn.handler.termsandconditions.internal;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.wso2.carbon.identity.application.authentication.framework.handler.request.PostAuthenticationHandler;
import org.wso2.carbon.identity.core.util.IdentityCoreInitializedEvent;
import org.yenlo.carbon.identity.post.authn.handler.termsandconditions.TermsAndConditionsPostAuthenticationHandler;
@Component(
name = "identity.post.authn.termsandconditions.handler",
immediate = true
)
public class TermsAndConditionsPostAuthnHandlerServiceComponent {
private static final Log log = LogFactory.getLog(TermsAndConditionsPostAuthnHandlerServiceComponent.class);
@Activate
protected void activate(ComponentContext context) {
try {
TermsAndConditionsPostAuthenticationHandler termsAndConditionsPostAuthenticationHandler =
new TermsAndConditionsPostAuthenticationHandler();
context.getBundleContext().registerService(PostAuthenticationHandler.class.getName(),
termsAndConditionsPostAuthenticationHandler, null);
} catch (Throwable e) {
log.error("Error while activating disclaimer post authentication handler.", e);
}
}
protected void unsetIdentityCoreInitializedEventService(IdentityCoreInitializedEvent identityCoreInitializedEvent) {
/* reference IdentityCoreInitializedEvent service to guarantee that this component will wait until identity core
is started */
}
@Reference(
name = "identity.core.init.event.service",
service = IdentityCoreInitializedEvent.class,
cardinality = ReferenceCardinality.MANDATORY,
policy = ReferencePolicy.DYNAMIC,
unbind = "unsetIdentityCoreInitializedEventService"
)
protected void setIdentityCoreInitializedEventService(IdentityCoreInitializedEvent identityCoreInitializedEvent) {
/* reference IdentityCoreInitializedEvent service to guarantee that this component will wait until identity core
is started */
}
}
JSP-Datei
<%@ page import="org.owasp.encoder.Encode" %>
<%@ page import="org.wso2.carbon.identity.application.authentication.endpoint.util.Constants" %>
<%
String app = request.getParameter("application");
String[] missingClaimList = null;
String appName = null;
if (request.getParameter(Constants.MISSING_CLAIMS) != null) {
missingClaimList = request.getParameter(Constants.MISSING_CLAIMS).split(",");
}
%>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WSO2 Identty Server</title>
<link rel="icon" href="images/favicon.png" type="image/x-icon"/>
<link href="libs/bootstrap_3.3.5/css/bootstrap.min.css" rel="stylesheet">
<link href="css/Roboto.css" rel="stylesheet">
<link href="css/custom-common.css" rel="stylesheet">
<!--[if lt IE 9]>
<script src="js/html5shiv.min.js"></script>
<script src="js/respond.min.js"></script>
<![endif]-->
</head>
<body>
<script type="text/javascript">
function approved() {
document.getElementById('consent').value = "approve";
document.getElementById("profile").submit();
}
function deny() {
document.getElementById('consent').value = "deny";
document.getElementById("profile").submit();
}
</script>
<!-- header -->
<header class="header header-default">
<div class="container-fluid"><br></div>
<div class="container-fluid">
<div class="pull-left brand float-remove-xs text-center-xs">
<a href="#">
<img src="images/logo-inverse.svg" alt="wso2" title="wso2" class="logo">
<h1><em> Identity Server </em></h1>
</a>
</div>
</div>
</header>
<!-- page content -->
<div class="container-fluid body-wrapper">
<div class="row">
<div class="col-md-12">
<!-- content -->
<div class="container col-xs-10 col-sm-6 col-md-6 col-lg-3 col-centered wr-content wr-login col-centered">
<div>
<h2 class="wr-title uppercase blue-bg padding-double white boarder-bottom-blue margin-none">
Terms & Conditions
</h2>
</div>
<div class="boarder-all ">
<div class="clearfix"></div>
<form action="../commonauth" method="post" id="profile" name=""
class="form-horizontal" >
<div class="padding-double login-form">
<div class="form-group">
<p>By using this <strong><%=Encode.forHtml(request.getParameter("application"))%></strong> app, you agree to the terms and conditions outlined below, which are designed to ensure the security and privacy of your personal data and provide a seamless user experience.
</p>
</div>
<table width="100%" class="styledLeft">
<tbody>
<tr>
<td class="buttonRow" colspan="2">
<div style="text-align:left;">
<input type="button" class="btn btn-primary" id="approve" name="approve"
onclick="javascript: approved(); return false;"
value="Approve"/>
<input class="btn" type="reset"
value="Deny"
onclick="javascript: deny(); return false;"/>
</div>
<input type="hidden" name="<%="sessionDataKey"%>"
value="<%=Encode.forHtmlAttribute(request.getParameter("sessionDataKey"))%>"/>
<input type="hidden" name="consent" id="consent"
value="deny"/>
</td>
</tr>
</tbody>
</table>
</div>
</form>
</div>
</div>
</div>
<!-- /content -->
</div>
</div>
<!-- /content/body -->
</div>
<!-- footer -->
<footer class="footer">
<div class="container-fluid">
<p>WSO2 Identity Server | ©
<script>document.write(new Date().getFullYear());</script>
<a href="http://wso2.com/" target="_blank"><i class="icon fw fw-wso2"></i>
Inc
</a>. All Rights Reserved
</p>
</div>
</footer>
<script src="libs/jquery_1.11.3/jquery-1.11.3.js"></script>
<script src="libs/bootstrap_3.3.5/js/bootstrap.min.js"></script>
</body>
</html>