In diesem Artikel befassen wir uns mit der Sicherheit von Spring und erstellen ein Authentifizierungssystem mit OAuth 2.0.
Spring Security ist ein leistungsstarkes, hochgradig anpassbares Framework zur Implementierung robuster Authentifizierungs- und Zugriffskontrollmechanismen in Java-basierten Anwendungen. Es ist eine Kernkomponente des Spring-?kosystems und wird h?ufig zur Sicherung von Webanwendungen, REST-APIs und anderen Backend-Diensten verwendet. Mit Spring Security erhalten Sie eine solide Grundlage für den Aufbau und die Durchsetzung sicherer Praktiken in Ihrer Anwendung.
So funktioniert Spring Security
Bevor wir uns mit der Funktionsweise von Spring Security befassen, ist es wichtig, den Lebenszyklus der Anforderungsverarbeitung in einem Java-basierten Webserver zu verstehen. Spring Security l?sst sich nahtlos in diesen Lebenszyklus integrieren, um eingehende Anfragen zu schützen.
Lebenszyklus der Anforderungsbearbeitung mit Spring Security
Der Lebenszyklus der Bearbeitung einer HTTP-Anfrage in einer Spring-basierten Anwendung mit Spring Security umfasst mehrere Phasen, von denen jede eine entscheidende Rolle bei der Verarbeitung, Validierung und Sicherung der Anfrage spielt.
1. Kundenanfrage
Der Lebenszyklus beginnt, wenn ein Client (z. B. Browser, mobile App oder API-Tool wie Postman) eine HTTP-Anfrage an den Server sendet.
Beispiel:
GET /api/admin/dashboard HTTP/1.1
2. Servlet-Container
Der Servlet-Container (z. B. Tomcat) empf?ngt die Anfrage und delegiert sie an das DispatcherServlet, den Front-Controller in einer Spring-Anwendung. Hier beginnt die Verarbeitungspipeline der Anwendung.
3. Feder-Sicherheitsfilterkette
Bevor das DispatcherServlet die Anfrage verarbeitet, wird sie von der Filterkette von Spring Security abgefangen. Die Filterkette ist eine Folge von Filtern, von denen jeder für die Bearbeitung bestimmter Sicherheitsaufgaben verantwortlich ist. Diese Filter stellen sicher, dass die Anfrage die Authentifizierungs- und Autorisierungsanforderungen erfüllt, bevor sie die Anwendungslogik erreicht.
Schlüsselfilter in der Kette:
Authentifizierungsfilter:
Diese Filter überprüfen, ob die Anfrage gültige Anmeldeinformationen enth?lt, z. B. einen Benutzernamen/ein Passwort, ein JWT oder Sitzungscookies.Autorisierungsfilter:
Nach der Authentifizierung stellen diese Filter sicher, dass der authentifizierte Benutzer über die erforderlichen Rollen oder Berechtigungen verfügt, um auf die angeforderte Ressource zuzugreifen.Andere Filter:
* **CsrfFilter**: Validates CSRF tokens to prevent Cross-Site Request Forgery attacks. * **CorsFilter**: Manages Cross-Origin Resource Sharing (CORS) rules for secure API access from different domains. * **ExceptionTranslationFilter**: Handles security-related exceptions (e.g., invalid credentials) and sends appropriate responses to the client.
4. Sicherheitskontext
Wenn die Authentifizierung erfolgreich ist, erstellt Spring Security ein Authentifizierungsobjekt und speichert es im SecurityContext. Auf dieses Objekt, das h?ufig in einem Thread-lokalen Speicher gespeichert ist, kann w?hrend des gesamten Anforderungslebenszyklus zugegriffen werden.
Das Authentifizierungsobjekt:
Principal: Stellt den authentifizierten Benutzer dar (z. B. Benutzername).
Anmeldeinformationen: Enth?lt Authentifizierungsdetails wie JWT-Tokens oder Passw?rter.
Autorit?ten: Enth?lt dem Benutzer zugewiesene Rollen und Berechtigungen.
Beispielablauf in der Filterkette:
Eine Anfrage durchl?uft die Authentifizierungsfilter.
Wenn die Anmeldeinformationen gültig sind, wird das Authentifizierungsobjekt erstellt und dem SecurityContext hinzugefügt.
Wenn die Anmeldeinformationen ungültig sind, sendet der ExceptionTranslationFilter eine 401 Unauthorized-Antwort an den Client.
5. DispatcherServlet
Sobald die Anfrage die Spring Security Filter Chain erfolgreich durchlaufen hat, übernimmt das DispatcherServlet:
Handler-Zuordnung:
Es ordnet die eingehende Anfrage basierend auf der URL und der HTTP-Methode der entsprechenden Controller-Methode zu.Controller-Aufruf:
Der zugeordnete Controller verarbeitet die Anfrage und gibt die entsprechende Antwort zurück, h?ufig mit Hilfe anderer Spring-Komponenten wie Dienste und Repositorys.
Wie Spring Security in den Lebenszyklus passt
Spring Security integriert sich über seine Filter in diesen Lebenszyklus und f?ngt Anfragen im frühesten Stadium ab. Wenn eine Anfrage die Anwendungslogik erreicht, ist sie bereits authentifiziert und autorisiert, wodurch sichergestellt wird, dass nur legitimer Datenverkehr von der Kernanwendung verarbeitet wird.
Das Design von Spring Security stellt sicher, dass Authentifizierung, Autorisierung und andere Sicherheitsma?nahmen deklarativ gehandhabt werden, was Entwicklern die Flexibilit?t gibt, das Verhalten nach Bedarf anzupassen oder zu erweitern. Es setzt nicht nur Best Practices durch, sondern vereinfacht auch die Umsetzung komplexer Sicherheitsanforderungen in modernen Anwendungen.
Federsicherheitskomponenten: Jenseits der Filterkette
Nachdem wir die Filterkette in Spring Security untersucht haben, wollen wir uns mit einigen anderen Schlüsselkomponenten befassen, die eine entscheidende Rolle im Authentifizierungs- und Autorisierungsprozess spielen.
AuthenticationManager
AuthenticationManager ist eine Schnittstelle, die eine einzelne Methode definiert, Authenticate(Authentication Authentication) , die verwendet wird, um die Anmeldeinformationen eines Benutzers zu überprüfen und festzustellen, ob sie gültig sind. Sie k?nnen sich AuthenticationManager als einen Koordinator vorstellen, bei dem Sie mehrere Anbieter registrieren k?nnen und der je nach Anforderungstyp eine Authentifizierungsanfrage an den richtigen Anbieter übermittelt.
AuthenticationProvider
Ein AuthenticationProvider ist eine Schnittstelle, die einen Vertrag zur Authentifizierung von Benutzern basierend auf ihren Anmeldeinformationen definiert. Es stellt einen bestimmten Authentifizierungsmechanismus dar, z. B. Benutzername/Passwort, OAuth oder LDAP. Es k?nnen mehrere AuthenticationProvider-Implementierungen nebeneinander existieren, sodass die Anwendung verschiedene Authentifizierungsstrategien unterstützen kann.
Kernkonzepte:
Authentifizierungsobjekt:
Der AuthenticationProvider verarbeitet ein Authentication-Objekt, das die Anmeldeinformationen des Benutzers (z. B. Benutzername und Passwort) kapselt.Authentifizierungsmethode:
Jeder AuthenticationProvider implementiert die Methode ?authenticate“ (Authentifizierungsauthentifizierung), in der sich die eigentliche Authentifizierungslogik befindet. Diese Methode:
* **CsrfFilter**: Validates CSRF tokens to prevent Cross-Site Request Forgery attacks. * **CorsFilter**: Manages Cross-Origin Resource Sharing (CORS) rules for secure API access from different domains. * **ExceptionTranslationFilter**: Handles security-related exceptions (e.g., invalid credentials) and sends appropriate responses to the client.
- unterstützt die Methode: Die Methode ?supports(Class> Authentication)“ gibt an, ob der AuthenticationProvider den angegebenen Authentifizierungstyp verarbeiten kann. Dadurch kann Spring Security den richtigen Anbieter für die Bearbeitung bestimmter Authentifizierungsanfragen ermitteln.
Beispiel:
Ein datenbankgestützter AuthenticationProvider validiert Benutzernamen und Passw?rter.
Ein OAuth-basierter AuthenticationProvider validiert von einem externen Identit?tsanbieter ausgegebene Token.
UserDetailsService
UserDetailsService?wird in der Spring-Dokumentation als Kernschnittstelle beschrieben, die benutzerspezifische Daten l?dt. Es enth?lt eine einzelne Methode ?loadUserByUsername“, die den Benutzernamen als Parameter akzeptiert und das Identit?tsobjekt ==User== zurückgibt. Grunds?tzlich erstellen wir eine Implementierungsklasse von UserDetailsService, in der wir die Methode ?loadUserByUsername“ überschreiben.
* Validates the user’s credentials. * Returns an authenticated `Authentication` object upon success. * Throws an `AuthenticationException` if authentication fails.
Wie all diese drei nun zusammenwirken, ist, dass AuthenticationManager AuthenticationProvider auffordert, die Authentifizierung entsprechend dem angegebenen Anbietertyp fortzusetzen, und die UserDetailsService-Implementierung hilft dem AuthenticationProvider beim Nachweis der Benutzerdetails.
Bevor wir nun mit der Konfiguration und all dem fortfahren, finden Sie hier einen kurzen überblick über Spring Security für die JWT-basierte Authentifizierung:
1. Benutzeranfrage
Der Benutzer sendet eine Anfrage mit seinen Anmeldeinformationen (Benutzername und Passwort) oder einem JWT-Token (im Header) an den authentifizierten Endpunkt und die Anfrage wird an den Authentifizierungsfilter weitergeleitet
-
AuthenticationFilter (z. B. UsernamePasswordAuthenticationFilter):
- Verwaltet die Benutzerauthentifizierung basierend auf den übermittelten Anmeldeinformationen (normalerweise in Form eines Benutzernamens und eines Passworts). Hier kommt der UsernamePasswordAuthenticationFilter ins Spiel.
- Es wartet auf die Anfrage, extrahiert den Benutzernamen und das Passwort und übergibt sie an den AuthenticationManager.
- Aber wir geben nicht den Benutzernamen und das Passwort weiter, sondern nur das Token. Daher sollte vor diesem AuthenticationFilter ein Filter vorhanden sein, der dem Authentifizierungsprozess mitteilt, dass der Benutzer authentifiziert ist und keine überprüfung auf Benutzernamen und Passwort usw. erforderlich ist erfolgt durch die Erstellung eines JWTFilter
2. JWTFilter
Dieser benutzerdefinierte Filter erweitert OncePerRequestFilter und wird vor UsernamePasswordAuthenticationFilter platziert. Er extrahiert das Token aus der Anfrage und validiert es.
Wenn das Token gültig ist, erstellt es ein UsernamePasswordAuthenticationToken und setzt dieses Token in den Sicherheitskontext, der der Spring-Sicherheit mitteilt, dass die Anfrage authentifiziert ist, und wenn diese Anfrage an den UsernamePasswordAuthenticationFilter weitergeleitet wird, wird sie einfach weitergeleitet, da sie das UsernamePasswordAuthenticationToken hat
* **CsrfFilter**: Validates CSRF tokens to prevent Cross-Site Request Forgery attacks. * **CorsFilter**: Manages Cross-Origin Resource Sharing (CORS) rules for secure API access from different domains. * **ExceptionTranslationFilter**: Handles security-related exceptions (e.g., invalid credentials) and sends appropriate responses to the client.
Dieses UsernamePasswordAuthenticationToken wird mit Hilfe von AuthenticationManager und AuthenticationProvider generiert, wenn wir Benutzernamen und Passwort anstelle des Tokens übergeben haben, nachdem wir den Benutzernamen und das Passwort mit Hilfe unserer UserDetails-Klasse authentifiziert haben.
3. AuthenticationManager
- AuthenticationManager: Dieser empf?ngt die Authentifizierungsanforderung und delegiert sie an den entsprechenden AuthenticationProvider, den wir konfigurieren.
* Validates the user’s credentials. * Returns an authenticated `Authentication` object upon success. * Throws an `AuthenticationException` if authentication fails.
4. AuthenticationProvider
UserDetailsService: Der AuthenticationProvider verwendet UserDetailsService, um Benutzerdetails basierend auf dem Benutzernamen zu laden. Und wir stellen dies mit einer Implementierung von UserDetailsService
bereit
Anmeldeinformationsvalidierung: Es vergleicht das bereitgestellte Passwort mit dem in den Benutzerdetails gespeicherten (normalerweise mit einem PasswordEncoder).
package com.oauth.backend.services; import com.oauth.backend.entities.User; import com.oauth.backend.repositories.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; @Component public class CustomUserDetailsService implements UserDetailsService { private final UserRepository userRepository; public CustomUserDetailsService(UserRepository userRepository) { this.userRepository = userRepository; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username); if(user==null){ throw new UsernameNotFoundException(username); } return new UserDetailsImpl(user); } public UserDetails loadUserByEmail(String email) throws UsernameNotFoundException { User user = userRepository.findByEmail(email); if(user==null){ throw new UsernameNotFoundException(email); } return new UserDetailsImpl(user); } }
Jetzt müssen all diese verschiedenen Filter und Beans konfiguriert werden, damit Spring Security wei?, was zu tun ist. Daher erstellen wir eine Konfigurationsklasse, in der wir die gesamte Konfiguration angeben.
@Component public class JWTFilter extends OncePerRequestFilter { private final JWTService jwtService; private final UserDetailsService userDetailsService; public JWTFilter(JWTService jwtService,UserDetailsService userDetailsService) { this.jwtService = jwtService; this.userDetailsService = userDetailsService; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final String authHeader = request.getHeader("Authorization"); if(authHeader == null || !authHeader.startsWith("Bearer")) { filterChain.doFilter(request,response); return; } final String jwt = authHeader.substring(7); final String userName = jwtService.extractUserName(jwt); Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if(userName !=null && authentication == null) { //Authenticate UserDetails userDetails = userDetailsService.loadUserByUsername(userName); if(jwtService.isTokenValid(jwt,userDetails)) { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); SecurityContextHolder.getContext() .setAuthentication(authenticationToken); } } filterChain.doFilter(request,response); } }
Bis jetzt haben wir unsere Authentifizierung mit Hilfe von Spring Security verstanden und konfiguriert, jetzt ist es an der Zeit, sie zu testen.
Wir erstellen eine einfache App mit zwei Controllern: AuthController (verwaltet Anmeldung und Registrierung) und ProductController (geschützter Dummy-Controller)
* **CsrfFilter**: Validates CSRF tokens to prevent Cross-Site Request Forgery attacks. * **CorsFilter**: Manages Cross-Origin Resource Sharing (CORS) rules for secure API access from different domains. * **ExceptionTranslationFilter**: Handles security-related exceptions (e.g., invalid credentials) and sends appropriate responses to the client.
* Validates the user’s credentials. * Returns an authenticated `Authentication` object upon success. * Throws an `AuthenticationException` if authentication fails.
package com.oauth.backend.services; import com.oauth.backend.entities.User; import com.oauth.backend.repositories.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; @Component public class CustomUserDetailsService implements UserDetailsService { private final UserRepository userRepository; public CustomUserDetailsService(UserRepository userRepository) { this.userRepository = userRepository; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username); if(user==null){ throw new UsernameNotFoundException(username); } return new UserDetailsImpl(user); } public UserDetails loadUserByEmail(String email) throws UsernameNotFoundException { User user = userRepository.findByEmail(email); if(user==null){ throw new UsernameNotFoundException(email); } return new UserDetailsImpl(user); } }
@Component public class JWTFilter extends OncePerRequestFilter { private final JWTService jwtService; private final UserDetailsService userDetailsService; public JWTFilter(JWTService jwtService,UserDetailsService userDetailsService) { this.jwtService = jwtService; this.userDetailsService = userDetailsService; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final String authHeader = request.getHeader("Authorization"); if(authHeader == null || !authHeader.startsWith("Bearer")) { filterChain.doFilter(request,response); return; } final String jwt = authHeader.substring(7); final String userName = jwtService.extractUserName(jwt); Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if(userName !=null && authentication == null) { //Authenticate UserDetails userDetails = userDetailsService.loadUserByUsername(userName); if(jwtService.isTokenValid(jwt,userDetails)) { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); SecurityContextHolder.getContext() .setAuthentication(authenticationToken); } } filterChain.doFilter(request,response); } }
@Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception{ return config.getAuthenticationManager(); }
@Bean public AuthenticationProvider authenticationProvider(){ DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); authenticationProvider.setUserDetailsService(userDetailsServiceImpl); authenticationProvider.setPasswordEncoder(passwordEncoder); return authenticationProvider; }
Bisher haben wir eine Registrierung, Anmeldung und Verifizierung implementiert, aber was ist, wenn ich auch die Funktion ?Anmeldung mit Google/Github“ hinzufügen m?chte, dann k?nnen wir dies mit Hilfe von OAuth2.0 tun
OAuth 2.0
OAuth 2.0 ist ein Protokoll zur Autorisierung, mit dem Benutzer Anwendungen Dritter Zugriff auf Ressourcen gew?hren k?nnen, die auf anderen Plattformen (z. B. Google Drive, Github) gespeichert sind, ohne die Anmeldeinformationen dieser Plattformen weiterzugeben.
Es wird haupts?chlich zum Aktivieren sozialer Anmeldungen wie ?Mit Google anmelden“ oder ?Mit Github anmelden“ verwendet.
Plattformen wie Google, Facebook und Github bieten einen Autorisierungsserver, der das OAuth 2.0-Protokoll für diese soziale Anmeldung oder Autorisierung des Zugriffs implementiert.
Schlüsselkonzepte von OAuth 2.0
Ressourceneigentümer
Kunde
Autorisierungsserver
Ressourcenserver
Zugriffstoken
Bereiche
Zuschüsse
Jetzt werden wir uns jedes Konzept einzeln ansehen
Ressourceneigentümer
Ressourceneigentümer ist der Benutzer, der die Drittanbieteranwendung (Ihre Anwendung) autorisieren m?chte.
Kunde
Es ist Ihre (Drittanbieter-)Anwendung, die auf die Daten oder Ressourcen vom Ressourcenserver zugreifen m?chte.
Ressourcenserver
Es ist der Server, auf dem die Daten des Benutzers gespeichert werden und auf den die Drittanbieteranwendung zugreifen kann.
Autorisierungsserver
Der Server, der den Ressourcenbesitzer authentifiziert und Zugriffstokens an den Client ausgibt (z. B. Google-Konten).
Zugriffstoken
Ein vom Autorisierungsserver an den Client ausgestellter Berechtigungsnachweis, der es ihm erm?glicht, im Namen des Benutzers auf den Ressourcenserver zuzugreifen. Es ist im Allgemeinen nur von kurzer Dauer und l?uft sehr bald ab. Daher wird auch ein Aktualisierungstoken bereitgestellt, um dieses Zugriffstoken zu aktualisieren, sodass der Benutzer sich nicht erneut autorisieren muss.
Bereiche
Spezifische vom Benutzer gew?hrte Berechtigungen, die definieren, was der Client mit den Daten des Benutzers tun kann und was nicht. Für die Autorisierung ben?tigen wir beispielsweise nur Benutzerinformationen wie Profil, Name usw., für den Dateizugriff ist jedoch ein anderer Bereich erforderlich.
Zuschüsse
Es bezieht sich auf die Methoden, mit denen die Clientanwendung das Zugriffstoken vom Autorisierungsserver erhalten kann. Eine Gew?hrung definiert den Prozess und die Bedingungen, unter denen die Clientanwendung berechtigt ist, auf die geschützten Daten eines Ressourcenbesitzers zuzugreifen.
Es ist sicher, da wir unser Client-Geheimnis und andere Anmeldeinformationen nicht dem Browser preisgeben müssen
Es gibt zwei am h?ufigsten verwendete Grant-Typen, die von OAuth 2.0 bereitgestellt werden
-
Autorisierungscode-Gew?hrung
Es ist die am h?ufigsten verwendete Art der Gew?hrung/Methode, die sicherste und eignet sich für serverseitige Anwendungen
Dabei wird vom Client ein Autorisierungscode an das Backend übergeben und das Backend gibt dem Client das Zugriffstoken.
Prozess:
- Der Client leitet den Benutzer zum Autorisierungsserver weiter.
- Der Benutzer meldet sich an und stimmt zu.
- Der Autorisierungsserver gibt einen Autorisierungscode aus.
- Der Client tauscht den Autorisierungscode mit dem Backend gegen ein Zugriffstoken aus.
-
Implizite Gew?hrung
Wird von Single-Page-Apps (SPAs) oder Anwendungen ohne Backend verwendet. Dabei wird das Zugriffstoken direkt im Browser selbst generiert und ausgegeben.
Prozess:
- Der Client leitet den Benutzer zum Autorisierungsserver weiter.
- Der Benutzer meldet sich an und stimmt zu.
- Der Autorisierungsserver stellt direkt ein Zugriffstoken aus.
Wir werden beide zum vollst?ndigen Verst?ndnis separat implementieren, aber zuerst werden wir die Autorisierungscode-Gew?hrung implementieren, die wir dafür ben?tigen
-
Autorisierungsserver
Es kann eine Plattform sein (wie Google, Github) oder Sie k?nnen Ihre eigene erstellen, auch mit KeyCloak, oder Sie k?nnen auch Ihre eigene erstellen, die den OAuth 2.0-Standards entspricht (vielleicht machen wir das im n?chsten Blog?)
-
Spring Boot-Anwendung
Dies wird unsere wichtigste Backend-Anwendung/-Dienst sein, die alle Vorg?nge wie Codeaustausch, überprüfung, Speichern von Benutzerdetails und Zuweisen von JWT-Tokens abwickelt
-
React-Anwendung (Frontend)
Dies wird unser Client sein, der den Benutzer zur Autorisierung an den Autorisierungsserver weiterleitet.
In unserer Implementierung werden wir also Folgendes tun: Das Frontend (Web/App) leitet unseren Benutzer zum Google-Login mit einer Umleitungs-URI zu unserem Backend-Endpunkt weiter, der die Kontrolle weiter übernimmt. Darüber werden wir sp?ter und zusammen mit der Redirect_URL sprechen Wir übergeben auch die Client-ID unserer App. All diese werden in den Abfrageparametern gesendet.
Nein, wenn sich der Benutzer erfolgreich bei Google anmeldet, leitet der Autorisierungsserver (Google) unsere Anfrage an den Backend-Enpoint weiter und dort tauschen wir den Autorisierungscode mit dem Autorisierungsserver aus, um Zugriffstoken und Aktualisierungstoken zu erhalten, und dann Wir k?nnen die Authentifizierung nach Belieben durchführen und schlie?lich eine Antwort an unser Frontend zurücksenden, das ein Cookie enth?lt und zu unserem Dashboard weiterleitet oder m?glicherweise eine geschützte Seite ist.
Jetzt schauen wir uns den Code an, aber stellen Sie sicher, dass Sie die URL Ihres Backend-Endpunkts in den autorisierten Weiterleitungs-URLs im Google-Konsolen-Dashboard für den OAuth-Client hinzufügen.
* **CsrfFilter**: Validates CSRF tokens to prevent Cross-Site Request Forgery attacks. * **CorsFilter**: Manages Cross-Origin Resource Sharing (CORS) rules for secure API access from different domains. * **ExceptionTranslationFilter**: Handles security-related exceptions (e.g., invalid credentials) and sends appropriate responses to the client.
Und das ist alles, es wird gut funktionieren und zum Testen k?nnen Sie eine einfache Frontend-Anwendung erstellen, die nichts anderes bietet, als einen Kontext zu haben und Sie kennen die Anmelde- und Registrierungsfunktionen.
Vielen Dank, dass Sie bis hierhin gelesen haben. Wenn Sie einen Vorschlag haben, schreiben Sie ihn bitte in die Kommentare
Das obige ist der detaillierte Inhalt vonSpring Security und OAuth verstehen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Hei?e KI -Werkzeuge

Undress AI Tool
Ausziehbilder kostenlos

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem v?llig kostenlosen KI-Gesichtstausch-Tool aus!

Hei?er Artikel

Hei?e Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Hei?e Themen

Der Unterschied zwischen HashMap und Hashtable spiegelt sich haupts?chlich in der Gewindesicherheit, der Nullwertunterstützung und der Leistung wider. 1. In Bezug auf die Gewindesicherheit ist Hashtable Thread-Safe, und seine Methoden sind haupts?chlich Synchronmethoden, w?hrend HashMap keine Synchronisationsverarbeitung durchführt, die nicht mit Thread-Safe ist. 2. In Bezug auf die Nullwertunterstützung erm?glicht HashMap einen Nullschlüssel und mehrere Nullwerte, w?hrend Hashtable keine Nullschlüssel oder -Werte zul?sst, sonst wird eine Nullpointerexception geworfen. 3. In Bezug auf die Leistung ist HashMap effizienter, da kein Synchronisationsmechanismus vorhanden ist und Hashtable für jeden Vorgang eine niedrige Verriegelungsleistung aufweist. Es wird empfohlen, stattdessen eine Concurrenthashmap zu verwenden.

Java verwendet Wrapper-Klassen, da grundlegende Datentypen nicht direkt an objektorientierten Operationen teilnehmen k?nnen und Objektformen h?ufig in den tats?chlichen Bedürfnissen erforderlich sind. 1. Sammelklassen k?nnen nur Objekte speichern, z. B. Listen verwenden automatische Boxen, um numerische Werte zu speichern. 2. Generika unterstützen keine Grundtypen, und Verpackungsklassen müssen als Typparameter verwendet werden. 3.. Verpackungsklassen k?nnen Nullwerte darstellen, um nicht festgelegte oder fehlende Daten zu unterscheiden. 4. Verpackungsklassen bieten praktische Methoden wie String -Conversion, um die Analyse und Verarbeitung von Daten zu erleichtern. In Szenarien, in denen diese Eigenschaften ben?tigt werden, sind Verpackungsklassen unverzichtbar.

Der JIT -Compiler optimiert den Code durch vier Methoden: Methode Inline, Hotspot -Erkennung und -vergleich, Typespekulation und Devirtualisation sowie die Eliminierung des redundanten Betriebs. 1. Methode Inline reduziert den Anrufaufwand und fügt h?ufig kleine Methoden direkt in den Anruf ein. 2. Erkennung und Hochfrequenzcodeausführung und zentral optimieren, um Ressourcen zu sparen. 3. Typ Spekulation sammelt Informationen zum Laufzeittyp, um Devirtualisation -Anrufe zu erzielen und die Effizienz zu verbessern. 4. Redundante Operationen beseitigen nutzlose Berechnungen und Inspektionen basierend auf den Betriebsdaten, wodurch die Leistung verbessert wird.

StaticMethodsinInterfaces -reisEtroducucuedInjava8toalloytilityFunctionSwitHinTheInterfaceItEp.beejava8, solche Funktionen, dieseparatehelperklassen, führendemTodisorganizedCode.Now, StaticMetheSprovidreefits: 1) theeneNableable -theenableaby

Instanzinitialisierungsbl?cke werden in Java verwendet, um die Initialisierungslogik beim Erstellen von Objekten auszuführen, die vor dem Konstruktor ausgeführt werden. Es ist für Szenarien geeignet, in denen mehrere Konstruktoren Initialisierungscode, komplexe Feldinitialisierung oder anonyme Szenarien der Klasseninitialisierung teilen. Im Gegensatz zu statischen Initialisierungsbl?cken wird es jedes Mal ausgeführt, wenn es instanziiert wird, w?hrend statische Initialisierungsbl?cke nur einmal ausgeführt werden, wenn die Klasse geladen wird.

InvaVa, theFinalKeywordPreventsAvariable von ValueFromBeingumedAfterasssignment, ButitsBehaviordiffersForprimitive und ANSPRIMITIVEVARIABLE, FinalMakesthevalueconstant, AsinfinalIntmax_speed = 100; WhirerastsignmentcausaSesSaSesSaSesSaSaSesSaSesSaSaSesSaSaSesSaSesSesirror

Es gibt zwei Arten von Konvertierung: implizit und explizit. 1. Die implizite Umwandlung erfolgt automatisch, wie z. B. das Konvertieren in INT in Doppel; 2. Explizite Konvertierung erfordert einen manuellen Betrieb, z. B. die Verwendung (int) MyDouble. Ein Fall, in dem die Typ -Konvertierung erforderlich ist, umfasst die Verarbeitung von Benutzereingaben, mathematische Operationen oder das übergeben verschiedener Werte zwischen Funktionen. Probleme, die beachtet werden müssen, sind: Umdrehung von Gleitpunktzahlen in Ganzzahlen wird der fraktionale Teil abschneiden, gro?e Typen in kleine Typen zu einem Datenverlust führen, und einige Sprachen erm?glichen keine direkte Konvertierung bestimmter Typen. Ein ordnungsgem??es Verst?ndnis der Regeln der Sprachkonvertierung hilft, Fehler zu vermeiden.

Der Werksmodus wird verwendet, um die Logik der Objekterstellung zusammenzufassen, wodurch der Code flexibler, einfach zu pflegen und locker gekoppelt ist. Die Kernantwort lautet: Durch zentrales Verwalten von Logik der Objekterstellung, das Ausblenden von Implementierungsdetails und die Unterstützung der Erstellung mehrerer verwandter Objekte. Die spezifische Beschreibung lautet wie folgt: Der Fabrikmodus gibt Objekterstellung an eine spezielle Fabrikklasse oder -methode zur Verarbeitung und vermeidet die Verwendung von NewClass () direkt; Es ist für Szenarien geeignet, in denen mehrere Arten von verwandten Objekten erstellt werden, die Erstellungslogik sich ?ndern und Implementierungsdetails versteckt werden müssen. Zum Beispiel werden im Zahlungsabwickler Stripe, PayPal und andere Instanzen durch Fabriken erstellt. Die Implementierung umfasst das von der Fabrikklasse zurückgegebene Objekt basierend auf Eingabeparametern, und alle Objekte erkennen eine gemeinsame Schnittstelle. Gemeinsame Varianten umfassen einfache Fabriken, Fabrikmethoden und abstrakte Fabriken, die für unterschiedliche Komplexit?ten geeignet sind.
