Security - THM-ATLAS/spring-backend Wiki

Das Backend ist durch Spring Security abgesichert. Dieses Framework wird von Spring zur Verfügung gestellt, um Zugriff auf bestimmte Funktionen von Anwendungen zu beschränken, und stellt Funktionalitäten zur Nutzerauthentifizierung zur Verfügung. Jede Request, die im Backend eintrifft, durchläuft zunächst die verschiedenen Stationen von Spring Security. Spring Security prüft anhand eines Auth-Headers, ob ein Nutzer authentifiziert ist. Ist dies der Fall, wird die Request zu den Controllern durchgestellt und entsprechend verarbeitet. Ist das nicht der Fall, wird der Nutzer zu /login weitergeleitet, und kann dort seinen Nutzernamen und Passwort eingeben.

AtlasAuthFilter

Der AtlasAuthFilter wird aufgerufen, wenn ein POST Request auf /login eintrifft. Er erweitert die Klasse UsernamePasswortAuthenticationFilter, welche die Default Klasse von Spring Security für Authentifizierung mit Benutzername und Passwort ist. Implementiert wird die Methode attepmtAuthentication. Diese nimmt eine HTTP Request und eine HTTP Response entgegen und gibt eine Authentication zurück. Innerhalb dieser Funktion wird die JSON im Payload der Request zu einem Objekt der Klasse AuthReq geparst, die den angegebenen Benutzernamen und das angegebene Passwort enthält. Diese Klasse gilt ausschließlich dazu, mit den Daten aus der JSON weiter arbeiten zu können. Aus Username und Passwort wird jetzt ein UsernamePasswordAuthenticationToken erzeugt, welches der authenticate Methode des LDAPAuthenticationManagers übergeben wird. Dieses Objekt repräsentiert eine versuchte Authentication, die noch geprüft werden muss.

LDAPAuthenticationManager

Der LDAPAuthenticationManager ist für die eigentliche Durchführung der Authentifizierung zuständig. Er implementiert das interface AuthenticationManager, welches die Methode authenticate implementiert haben möchte. Diese Methode nimmt eine Authentication entgegen (in unserem Fall das UsernamePasswordAuthenticationToken) und gibt eine Authentication wieder zurück (In unsrem Fall eine AtlasAuthentication). Diese Methode prüft Benutzernamen und Passwort, und setzt das Feld authenticated in der Authentication auf true, wenn alles passt. In unserem Fall holt die Methode sich Benutzernamen und Passwort aus dem UsernamePasswordAuthenticationToken und prüft dann zunächst, ob dieser Nutzername im THM LDAP bekannt ist. Wenn ja, wird die Authentifizierung über das LDAP probiert. Wenn nein wird eine Authentifizierung über die Datenbank probiert. Funktioniert beides nicht, wird eine InvalidCredentialsException geworfen. War die Authentifizierung erfolgreich, werden die UserDetails aus der Datenbank bzw. dem LDAP geholt und aktualisiert. Danach wird eine neue AtlasAuthentication mit dem entsprechenden Nutzer erstellt und authenticated auf true gesetzt.

initLdap()

Die Methode initLdap() initialisiert eine Verbindung mit dem LDAP Server der THM. Zurückgegeben wird ein LdapTemplate das bereits mit der URL und dem Root-Verzeichnis des Servers ausgestattet ist. Mit diesem Template können sowohl Nutzer authentifiziert werden als auch Nutzerdaten aus dem LDAP zur Verwendung in der Datenbank abgerufen werden.

findUserDn()

Die Anmeldung via LDAP ist nur mit dem vollständigen Distinguished-Name (DN) des Nutzers möglich. Der DN ist der vollständige Pfad vom Root-Verzeichnis des Servers zum Speicherort des Nutzers. Da die meisten Nutzer ihren DN nicht kennen und er nicht einfach aus dem Nutzernamen ableitbar ist, muss im LDAP nach dem Nutzer gesucht werden. findUserDn() verwendet dazu den Nutzernamen (uid) des Nutzers und sucht ausgehend von Root nach allen Objekten die der Objektklasse gifb-person zugeordnet sind und deren uid mit der des Nutzers übereinstimmt. Zurückgegeben wird der vollständige DN des Nutzers.

getUserProperties()

Die Methode getUserProperties() sucht genau wie findUserDn() von Root ausgehend nach dem Nutzer, gibt jedoch nicht dessen DN zurück sondern alle Attribute die für den AtlasUser wichtig sind. Das sind aktuell der vollständige Name und die E-Mail.

Atlas Authentication

Die AtlasAuthentication ist unsere Implementierung des Spring Interfaces Authentication. Dieses Interface bietet einige Methoden an, die wichtig für die Nutzerverwaltung sind. Die Methode isAuthenticated gibt zurück, ob ein Nutzer bereits erfolgreich vom LDAPAuthenticationManager authentifiziert wird, setAuthenticated wird com LDAPAuthenticationManager genutzt, um auhenticated auf true zu setzen. Außerdem implementieren wir die Methoden getName, die den Nutzernamen des Nutzers zurückgibt, getAuthorities, die eine Liste an Nutzerrollen zurück gibt, getCredentials, die, im Falle von selbst verwalteten Nutzern das durch BCrypt verschlüsselte Passwort zurückgibt und im Falle von LDAP Nutzern null, sowie getDetails, die wir nicht nutzen und daher null zurück geben. Außerdem implementieren wir noch die Methode getPrincipal, welche den angemeldeten AtlasUser zurückgibt.

Diese Klasse wird von Spring Security dazu genutzt, die Session eines Users zu verwalten. In ihr ist alles enthalten, was für Sessions wichtig ist: der Nutzer, der angemeldet ist, dessen Rollen und ob eine Authentifizierung über LDAP oder Datenbank stattgefunden hat.

UserDetailsService

Der UserDetailsService implementiert das Spring Interface UserDetailsService. Das bietet eine Methode loadUserByUsername an, die vom LDAPAuthenticationManager verwendet wird, um den AtlasUser zu einem angegebenen Benutzernamen zu finden. Dazu wird in die Datenbank geschaut, ob ein Nutzer mit diesem Nutzernamen existiert. Wenn ja, werden die Rollen des Nutzers aus der Datenbank gezogen und befüllt, sowie das Passwort aus der Datenbank gezogen und befüllt, bzw. auf einen leeren String gesetzt. Dazu gilt: Wir speichern selbstverständlich keine Passwörter im Klartext. Alle Passwörter sind mit BCrypt verschlüsselt, und werden nur so gespeichert oder verarbeitet. LDAP Passwörter speichern wir generell nicht, die entsprechenden Felder in der Datenbank sind null. Dieser User wird dann zurückgegeben.

AtlasUser

Der AtlasUser repräsentiert einen Nutzer der Lernplattform. Dieser hat die in der Datenbank gespeicherten Attribute user_id, name, username und email. Diese Attribute können mithilfe des UserRepository direkt aus der Datenbank geholt werden. Zusätzlich implementiert der AtlasUser das Interface UserDetails. Das Interface repräsentiert in Spring Security einen User. Auf diesen User kann in Controllern mit der Annotation @AuthenticationPrincipal zugegriffen werden, um beispielsweise Nutzerrollen zu prüfen. Das Interface möchte einige Methoden implementiert haben: getAuthorities gibt die Nutzerrollen zurück, bei uns repräsentiert durch die Klasse Role, getPassword gibt das Passwort (von BCrypt verschlüsselt) zurück (hier gilt wieder die Anmerkung von oben) und getUsername gibt den Benutzernamen zurück. die Methoden isAccountNonExpired, isAccountNonLocked, isCredentialsNonExpired und isEnabled geben aktuell immer true zurück, weil wir entsprechende Funktionen noch nicht anbieten.

Role

Eine Role repräsentiert eine Rolle in ATLAS, die mit entsprechenden Berechtigungen verknüpft ist. Die Klasse Role implementiert dazu das Interface GrantedAuthority, welches die Methode getAuthority implementiert haben möchte, welche einen String, in unserem Fall den Namen der Rolle, zurück gibt. Mithilfe dieser Rollen prüfen wir in den Controllern, ob ein Nutzer bestimmte Funktionen nutzen darf.

SecurityConfig

Die SecurityConfig konfiguriert Eigenschaften der Klasse HttpSecurity. Wir deaktiveiren CRSF, um POST, PUT und DELETE anbieten zu können. Auch werden dort der LDAPAuthenticationManager, sowie der AtlasAuthFilter registriert. Anschließend wird festgelegt, dass unangemeldete Nutzer lediglich Zugriff auf die Pfade /login, /api/users (zum Registrieren), sowie das Frontend und die API Doc haben. Alle anderen Requests funktionieren nur nach erfolgreicher Authentifikation. Als Werkzeug für die Authentifikation wird der Pfad /login festgelegt, man wird also beim Versuch eine gesicherte Seite aufzurufen automatisch zu /login durchgestellt, und /login wird als Pfad festgelegt, auf den die POST Request mit Username und Passwort geht.