Wenn es sich um ein einzelnes Webprojekt handelt, können Informationen zum Status einer bestimmten Interaktionssitzung zwischen dem Client und dem Server mithilfe der Benutzerauthentifizierung bei seiner Anmeldung leicht verwaltet werden. Wenn sich jedoch ein solches unabhängiges System entwickelt und sich in mehrere Systeme verwandelt, steht der Entwickler vor der Frage, Informationen über den Status jedes dieser separaten Systeme zu erhalten. In der Praxis sieht diese Frage folgendermaßen aus: "Muss der Benutzer dieser Systeme jedes einzeln eingeben und auch verlassen?"
Es gibt eine gute Faustregel für Systeme, deren Komplexität im Laufe der Zeit zunimmt, und für die Interaktion dieser Systeme mit ihren Benutzern. Die Last der Lösung von Problemen, die mit der Komplikation der Projektarchitektur verbunden sind, liegt nämlich beim System und nicht bei seinen Benutzern. Es spielt keine Rolle, wie komplex die internen Mechanismen des Webprojekts sind. Für den Benutzer sollte es wie ein einheitliches System aussehen. Mit anderen Worten, ein Benutzer, der mit einem aus vielen Komponenten bestehenden Websystem arbeitet, muss wahrnehmen, was passiert, als würde er mit einem System arbeiten. Insbesondere geht es um die Authentifizierung in solchen Systemen mithilfe von SSO (Single Sign-On) - einer Single Sign-On-Technologie.
Wie erstelle ich Systeme, die SSO verwenden? Sie könnten hier an die gute alte Cookie-basierte Lösung denken, aber diese Lösung unterliegt Einschränkungen. Die Einschränkungen gelten für die Domains, von denen aus Cookies gesetzt werden. Es kann nur umgangen werden, indem alle Domänennamen aller Subsysteme der Webanwendung in einer Domäne der obersten Ebene gesammelt werden.
In der heutigen Umgebung werden solche Lösungen durch die weit verbreitete Einführung von Microservice-Architekturen behindert. Das Sitzungsmanagement wurde zu einer Zeit komplizierter, als bei der Entwicklung von Webprojekten unterschiedliche Technologien verwendet wurden und manchmal unterschiedliche Dienste auf unterschiedlichen Domänen gehostet wurden. Darüber hinaus begannen Webdienste, die früher in Java geschrieben wurden, mit den Funktionen der Node.js-Plattform zu schreiben. Dies machte es schwieriger, mit Cookies zu arbeiten. Es stellte sich heraus, dass Sitzungen jetzt nicht mehr so einfach zu verwalten sind.
Diese Schwierigkeiten haben zur Entwicklung neuer Methoden für die Anmeldung in Systemen geführt. Insbesondere handelt es sich um die Single-Sign-On-Technologie.
Single Sign-On-Technologie
Das Grundprinzip, auf dem die Single Sign-On-Technologie basiert, besteht darin, dass sich ein Benutzer bei einem System eines Projekts anmelden kann, das aus vielen Systemen besteht, und in allen anderen Systemen autorisiert werden kann, ohne sich erneut anmelden zu müssen. Gleichzeitig sprechen wir von einem zentralen Ausstieg aus allen Systemen.
Zu Bildungszwecken werden wir die SSO-Technologie auf der Node.js-Plattform implementieren.
Es sollte beachtet werden, dass die Implementierung dieser Technologie im Unternehmensmaßstab viel mehr Aufwand erfordert, als wir in die Entwicklung unseres Schulungssystems investieren werden. Aus diesem Grund gibt es spezielle SSO-Lösungen für Großprojekte.
Wie ist die SSO-Anmeldung organisiert?
Das Herzstück der SSO-Implementierung ist ein einzelner unabhängiger Authentifizierungsserver, der Informationen zur Authentifizierung von Benutzern akzeptieren kann. Zum Beispiel - E-Mail-Adresse, Benutzername, Passwort. Andere Systeme bieten dem Benutzer keine direkten Mechanismen, um sich bei ihnen anzumelden. Sie autorisieren den Benutzer indirekt, indem sie Informationen über ihn vom Authentifizierungsserver erhalten. Indirekte Autorisierungsmechanismen werden mithilfe von Token implementiert.
Hier ist das Code-Repository für das simple-sso-Projekt, dessen Implementierung ich hier beschreiben werde. Ich verwende das Node.js-Framework, aber Sie können dasselbe mit etwas anderem implementieren. Lassen Sie uns Schritt für Schritt die Aktionen des Benutzers, der mit dem System arbeitet, und die Mechanismen, aus denen dieses System besteht, analysieren.
Schritt 1
Der Benutzer versucht, auf eine geschützte Ressource im System zuzugreifen (nennen wir diese Ressource "SSO-Consumer", "sso-consumer"). Der SSO-Verbraucher stellt fest, dass der Benutzer nicht angemeldet ist, und leitet den Benutzer unter Verwendung seiner eigenen Adresse als Abfrageparameter zum "SSO-Server" ("sso-server") weiter. Ein erfolgreich authentifizierter Benutzer wird an diese Adresse umgeleitet. Dieser Mechanismus wird von der Express-Middleware bereitgestellt:
const isAuthenticated = (req, res, next) => {
// , ,
// - SSO-
// URL URL,
// ,
const redirectURL = `${req.protocol}://${req.headers.host}${req.path}`;
if (req.session.user == null) {
return res.redirect(
`http://sso.ankuranand.com:3010/simplesso/login?serviceURL=${redirectURL}`
);
}
next();
};
module.exports = isAuthenticated;
Schritt 2
Der SSO-Server stellt fest, dass der Benutzer nicht angemeldet ist, und leitet ihn zur Anmeldeseite weiter:
const login = (req, res, next) => {
// req.query url,
// , sso-.
//
//
const { serviceURL } = req.query;
// URL.
if (serviceURL != null) {
const url = new URL(serviceURL);
if (alloweOrigin[url.origin] !== true) {
return res
.status(400)
.json({ message: "Your are not allowed to access the sso-server" });
}
}
if (req.session.user != null && serviceURL == null) {
return res.redirect("/");
}
// -
//
if (req.session.user != null && serviceURL != null) {
const url = new URL(serviceURL);
const intrmid = encodedId();
storeApplicationInCache(url.origin, req.session.user, intrmid);
return res.redirect(`${serviceURL}?ssoToken=${intrmid}`);
}
return res.render("login", {
title: "SSO-Server | Login"
});
};
Ich werde hier einige Kommentare zur Sicherheit abgeben.
Wir überprüfen den
serviceURLParameter für eingehende Anforderungen an den SSO-Server. Auf diese Weise können wir herausfinden, ob diese URL im System registriert ist und ob der von ihr dargestellte Dienst die Dienste eines SSO-Servers verwenden kann.
So könnte die Liste der URLs für Dienste aussehen, die den SSO-Server verwenden dürfen:
const alloweOrigin = {
"http://consumer.ankuranand.in:3020": true,
"http://consumertwo.ankuranand.in:3030": true,
"http://test.tangledvibes.com:3080": true,
"http://blog.tangledvibes.com:3080": fasle,
};
Schritt 3
Der Benutzer gibt in der Anmeldeanforderung einen Benutzernamen und ein Kennwort ein, die an den SSO-Server gesendet werden.

Loginseite
Schritt 4
Der SSO-Authentifizierungsserver überprüft die Benutzerinformationen und erstellt eine Sitzung zwischen sich und dem Benutzer. Dies ist die sogenannte "globale Sitzung". Ein Autorisierungstoken wird sofort erstellt. Das Token ist eine Folge von zufälligen Zeichen. Wie genau diese Zeichenfolge generiert wird, spielt keine Rolle. Die Hauptsache ist, dass ähnliche Zeilen nicht für verschiedene Benutzer wiederholt werden und dass es schwierig wäre, eine solche Zeile zu fälschen.
Schritt 5
Der SSO-Server nimmt das Autorisierungstoken und leitet es an die Stelle weiter, von der der neu angemeldete Benutzer stammt (dh, er übergibt das Token an den SSO-Verbraucher).
const doLogin = (req, res, next) => {
// .
// ,
// userDB - , ,
const { email, password } = req.body;
if (!(userDB[email] && password === userDB[email].password)) {
return res.status(404).json({ message: "Invalid email and password" });
}
//
const { serviceURL } = req.query;
const id = encodedId();
req.session.user = id;
sessionUser[id] = email;
if (serviceURL == null) {
return res.redirect("/");
}
const url = new URL(serviceURL);
const intrmid = encodedId();
storeApplicationInCache(url.origin, id, intrmid);
return res.redirect(`${serviceURL}?ssoToken=${intrmid}`);
};
Wieder einige Sicherheitshinweise:
- Dieses Token sollte immer als Zwischenmechanismus betrachtet werden. Es wird verwendet, um ein anderes Token zu erhalten.
- Wenn Sie das JWT als Zwischen-Token verwenden, versuchen Sie, keine Geheimnisse darin aufzunehmen.
Schritt 6
Der SSO-Verbraucher empfängt ein Token und kontaktiert den SSO-Server, um das Token zu überprüfen. Der Server überprüft das Token und gibt ein weiteres Token mit Benutzerinformationen zurück. Dieses Token wird vom SSO-Konsumenten verwendet, um eine Sitzung mit dem Benutzer zu erstellen. Diese Sitzung wird als lokal bezeichnet.
Hier ist der Middleware-Code, der im Express-basierten SSO-Consumer verwendet wird:
const ssoRedirect = () => {
return async function(req, res, next) {
// , req queryParameter, ssoToken,
// , .
const { ssoToken } = req.query;
if (ssoToken != null) {
// ssoToken , .
const redirectURL = url.parse(req.url).pathname;
try {
const response = await axios.get(
`${ssoServerJWTURL}?ssoToken=${ssoToken}`,
{
headers: {
Authorization: "Bearer l1Q7zkOL59cRqWBkQ12ZiGVW2DBL"
}
}
);
const { token } = response.data;
const decoded = await verifyJwtToken(token);
// jwt,
// global-session-id id ,
// .
req.session.user = decoded;
} catch (err) {
return next(err);
}
return res.redirect(`${redirectURL}`);
}
return next();
};
};
Nach dem Empfang einer Anforderung von einem SSO-Verbraucher überprüft der Server das Token auf Existenz und Ablaufdatum. Das validierte Token wird als gültig angesehen.
In unserem Fall gibt der SSO-Server nach erfolgreicher Überprüfung des Tokens eine signierte JWT mit Informationen zum Benutzer zurück.
const verifySsoToken = async (req, res, next) => {
const appToken = appTokenFromRequest(req);
const { ssoToken } = req.query;
// ssoToken .
// ssoToken - , .
if (
appToken == null ||
ssoToken == null ||
intrmTokenCache[ssoToken] == null
) {
return res.status(400).json({ message: "badRequest" });
}
// appToken -
const appName = intrmTokenCache[ssoToken][1];
const globalSessionToken = intrmTokenCache[ssoToken][0];
// appToken , SSO-
if (
appToken !== appTokenDB[appName] ||
sessionApp[globalSessionToken][appName] !== true
) {
return res.status(403).json({ message: "Unauthorized" });
}
// ,
const payload = generatePayload(ssoToken);
const token = await genJwtToken(payload);
// ,
delete intrmTokenCache[ssoToken];
return res.status(200).json({ token });
};
Hier sind einige Sicherheitshinweise.
- Alle Anwendungen, die diesen Server zur Authentifizierung verwenden, müssen beim SSO-Server registriert sein. Ihnen müssen Codes zugewiesen werden, mit denen sie überprüft werden, wenn sie Anforderungen an den Server stellen. Dies ermöglicht ein höheres Sicherheitsniveau bei der Kommunikation zwischen dem SSO-Server und den SSO-Verbrauchern.
- Es ist möglich, für jede Anwendung unterschiedliche "private" und "öffentliche" rsa-Dateien zu generieren und jede von ihnen ihre JWTs intern mit ihren jeweiligen öffentlichen Schlüsseln überprüfen zu lassen.
Darüber hinaus können Sie eine Sicherheitsrichtlinie auf Anwendungsebene definieren und deren zentralen Speicher organisieren:
const userDB = {
"info@ankuranand.com": {
password: "test",
userId: encodedId(), // , .
appPolicy: {
sso_consumer: { role: "admin", shareEmail: true },
simple_sso_consumer: { role: "user", shareEmail: false }
}
}
};
Nachdem sich der Benutzer erfolgreich beim System angemeldet hat, werden Sitzungen zwischen ihm und dem SSO-Server sowie zwischen ihm und jedem Subsystem erstellt. Die zwischen dem Benutzer und dem SSO-Server eingerichtete Sitzung wird als globale Sitzung bezeichnet. Eine zwischen einem Benutzer und einem Subsystem eingerichtete Sitzung, die dem Benutzer einige Dienste bereitstellt, wird als lokale Sitzung bezeichnet. Nachdem die lokale Sitzung eingerichtet wurde, kann der Benutzer mit den Subsystemressourcen arbeiten, die für externe Ressourcen geschlossen sind.
Einrichten lokaler und globaler Sitzungen
Eine kurze Tour durch den SSO-Consumer und den SSO-Server
Lassen Sie uns einen kurzen Überblick über die Funktionen von SSO-Verbrauchern und SSO-Servern geben.
▍ SSO-Verbraucher
- Das SSO-Consumer-Subsystem authentifiziert den Benutzer nicht, indem es den Benutzer zum SSO-Server umleitet.
- Dieses Subsystem empfängt das vom SSO-Server an ihn übergebene Token.
- Es interagiert mit dem Server, um die Gültigkeit des Tokens zu überprüfen.
- Sie empfängt die JWT und validiert dieses Token mit dem öffentlichen Schlüssel.
- Dieses Subsystem richtet eine lokale Sitzung ein.
▍SSO Server
- Der SSO-Server überprüft die Benutzeranmeldeinformationen.
- Der Server erstellt eine globale Sitzung.
- Es wird ein Autorisierungstoken erstellt.
- Ein Autorisierungstoken wird an den SSO-Verbraucher gesendet.
- Der Server überprüft die Gültigkeit der von SSO-Verbrauchern an ihn übergebenen Token.
- Der Server sendet eine SSO-JWT mit Benutzerinformationen an den Verbraucher.
Organisation der zentralen Abmeldung
Ähnlich wie bei der Implementierung von SSO können Sie die SSO-Technologie implementieren. Hier müssen Sie nur die folgenden Überlegungen berücksichtigen:
- Wenn eine lokale Sitzung vorhanden ist, muss auch eine globale Sitzung vorhanden sein.
- Wenn eine globale Sitzung vorhanden ist, bedeutet dies nicht unbedingt, dass eine lokale Sitzung vorhanden ist.
- Wenn die lokale Sitzung zerstört wird, muss auch die globale Sitzung zerstört werden.
Ergebnis
Infolgedessen kann festgestellt werden, dass es viele vorgefertigte Implementierungen der Single Sign-On-Technologie gibt, die Sie in Ihr System integrieren können. Sie alle haben ihre eigenen Vor- und Nachteile. Die Entwicklung eines solchen Systems unabhängig von Grund auf ist ein iterativer Prozess, bei dem Sie die Eigenschaften jedes Systems analysieren müssen. Dies umfasst Anmeldemethoden, Speicherung von Benutzerinformationen, Datensynchronisation und mehr.
Verwenden Ihre Projekte SSO-Mechanismen?
