Verwenden von Spring Boot für OAuth2 und JWT-REST-Schutz

Veröffentlicht: 2022-03-11

Dieser Artikel ist eine Anleitung zum Einrichten einer serverseitigen Implementierung von JSON Web Token (JWT) – OAuth2-Autorisierungsframework mit Spring Boot und Maven.

Ein erstes Verständnis von OAuth2 wird empfohlen und kann durch Lesen des oben verlinkten Entwurfs oder durch Suchen nach nützlichen Informationen im Internet wie dieser oder dieser erlangt werden.

OAuth2 ist ein Autorisierungsframework, das die erste Version von OAuth ersetzt, die bereits 2006 erstellt wurde. Es definiert die Autorisierungsflüsse zwischen Clients und einem oder mehreren HTTP-Diensten, um Zugriff auf geschützte Ressourcen zu erhalten.

OAuth2 definiert die folgenden serverseitigen Rollen:

  • Ressourceneigentümer: Der Dienst, der für die Steuerung des Ressourcenzugriffs verantwortlich ist
  • Ressourcenserver: Der Dienst, der die Ressourcen tatsächlich bereitstellt
  • Autorisierungsserver: Der Dienst, der den Autorisierungsprozess abwickelt und als Mittelsmann zwischen Client und Ressourceneigentümer fungiert

JSON Web Token oder JWT ist eine Spezifikation für die Darstellung von Ansprüchen, die zwischen zwei Parteien übertragen werden sollen. Die Ansprüche werden als JSON-Objekt codiert, das als Nutzlast einer verschlüsselten Struktur verwendet wird, sodass die Ansprüche digital signiert oder verschlüsselt werden können.

Die enthaltende Struktur kann JSON Web Signature (JWS) oder JSON Web Encryption (JWE) sein.

JWT kann als Format für Zugriffs- und Aktualisierungstoken ausgewählt werden, die innerhalb des OAuth2-Protokolls verwendet werden.

OAuth2 und JWT haben in den letzten Jahren aufgrund der folgenden Funktionen eine große Popularität erlangt:

  • Bietet ein zustandsloses Autorisierungssystem für das zustandslose REST-Protokoll
  • Passt gut in eine Microservice-Architektur, in der mehrere Ressourcenserver einen einzigen Autorisierungsserver gemeinsam nutzen können
  • Token-Inhalte auf Kundenseite dank JSON-Format einfach zu verwalten

OAuth2 und JWT sind jedoch nicht immer die beste Wahl, wenn die folgenden Überlegungen für das Projekt wichtig sind:

  • Ein zustandsloses Protokoll erlaubt keine Zugriffssperrung auf der Serverseite
  • Eine feste Lebensdauer für Token erhöht die Komplexität für die Verwaltung lang andauernder Sitzungen, ohne die Sicherheit zu beeinträchtigen (z. B. Token aktualisieren).
  • Eine Anforderung für einen sicheren Speicher für ein Token auf der Clientseite

Erwarteter Protokollfluss

Während eines der Hauptmerkmale von OAuth2 die Einführung einer Autorisierungsschicht ist, um den Autorisierungsprozess von den Ressourceneigentümern zu trennen, ist das Ergebnis des Artikels der Einfachheit halber der Aufbau einer einzelnen Anwendung, die alle Ressourceneigentümer , Autorisierungsserver und alle imitiert Ressourcenserverrollen . Aus diesem Grund fließt die Kommunikation nur zwischen zwei Entitäten, dem Server und dem Client.

Diese Vereinfachung soll helfen, sich auf das Ziel des Artikels zu konzentrieren, nämlich die Einrichtung eines solchen Systems in einer Spring-Boot-Umgebung.

Der vereinfachte Ablauf wird im Folgenden beschrieben:

  1. Die Autorisierungsanfrage wird vom Client an den Server (der als Ressourceneigentümer fungiert) unter Verwendung der Kennwortautorisierungserteilung gesendet
  2. Das Zugriffstoken wird an den Client zurückgegeben (zusammen mit dem Aktualisierungstoken)
  3. Das Zugriffstoken wird dann bei jeder Anforderung für den Zugriff auf geschützte Ressourcen vom Client zum Server (der als Ressourcenserver fungiert) gesendet
  4. Der Server antwortet mit den erforderlichen geschützten Ressourcen

Authentifizierungs-Flussdiagramm

Spring Security und Spring Boot

Zunächst eine kurze Einführung in den für dieses Projekt ausgewählten Technologie-Stack.

Das Projektmanagement-Tool der Wahl ist Maven, aber aufgrund der Einfachheit des Projekts sollte es nicht schwierig sein, auf andere Tools wie Gradle umzusteigen.

In der Fortsetzung des Artikels konzentrieren wir uns nur auf Spring Security-Aspekte, aber alle Codeauszüge stammen aus einer voll funktionsfähigen serverseitigen Anwendung, deren Quellcode in einem öffentlichen Repository zusammen mit einem Client verfügbar ist, der seine REST-Ressourcen verbraucht.

Spring Security ist ein Framework, das nahezu deklarative Sicherheitsdienste für Spring-basierte Anwendungen bereitstellt. Seine Wurzeln liegen in den ersten Anfängen von Spring und es ist aufgrund der hohen Anzahl unterschiedlicher abgedeckter Sicherheitstechnologien als eine Reihe von Modulen organisiert.

Werfen wir einen kurzen Blick auf die Architektur von Spring Security (eine ausführlichere Anleitung finden Sie hier).

Bei der Sicherheit geht es vor allem um die Authentifizierung , also die Überprüfung der Identität, und die Autorisierung , die Gewährung von Zugriffsrechten auf Ressourcen.

Spring Security unterstützt eine große Auswahl an Authentifizierungsmodellen, die entweder von Drittanbietern bereitgestellt oder nativ implementiert werden. Eine Liste finden Sie hier.

Hinsichtlich der Zulassung werden drei Hauptbereiche identifiziert:

  1. Web fordert Autorisierung an
  2. Autorisierung auf Methodenebene
  3. Zugriff auf die Autorisierung von Domänenobjektinstanzen

Authentifizierung

Die grundlegende Schnittstelle ist AuthenticationManager , der für die Bereitstellung einer Authentifizierungsmethode verantwortlich ist. Der UserDetailsService ist die Schnittstelle, die sich auf die Sammlung von Benutzerinformationen bezieht, die direkt implementiert oder intern im Falle von Standard-JDBC- oder LDAP-Methoden verwendet werden könnte.

Genehmigung

Die Hauptschnittstelle ist AccessDecisionManager ; welche Implementierungen für alle drei oben aufgeführten Bereiche an eine Kette von AccessDecisionVoter . Jede Instanz der letztgenannten Schnittstelle stellt eine Zuordnung zwischen einer Authentication (einer Benutzeridentität, benannter Prinzipal), einer Ressource und einer Sammlung von ConfigAttribute , dem Regelsatz, der beschreibt, wie der Eigentümer der Ressource den Zugriff auf die Ressource selbst gewährt hat, möglicherweise durch die Verwendung von Benutzerrollen.

Die Sicherheit für eine Webanwendung wird mithilfe der oben beschriebenen Grundelemente in einer Kette von Servlet-Filtern implementiert, und die Klasse WebSecurityConfigurerAdapter wird als deklarative Methode bereitgestellt, um die Zugriffsregeln der Ressource auszudrücken.

Die Methodensicherheit wird zunächst durch das Vorhandensein der Annotation @EnableGlobalMethodSecurity(securedEnabled = true) und dann durch die Verwendung einer Reihe spezialisierter Annotationen aktiviert, die auf jede zu schützende Methode angewendet werden, z. B. @Secured , @PreAuthorize und @PostAuthorize .

Spring Boot fügt all dem eine Sammlung eigensinniger Anwendungskonfigurationen und Bibliotheken von Drittanbietern hinzu, um die Entwicklung zu erleichtern und gleichzeitig einen hohen Qualitätsstandard aufrechtzuerhalten.

JWT OAuth2 mit Spring Boot

Kommen wir nun zum ursprünglichen Problem, eine Anwendung einzurichten, die OAuth2 und JWT mit Spring Boot implementiert.

Während es in der Java-Welt mehrere serverseitige OAuth2-Bibliotheken gibt (eine Liste finden Sie hier), ist die Spring-basierte Implementierung die natürliche Wahl, da wir davon ausgehen, dass sie gut in die Spring Security-Architektur integriert ist und daher die Notwendigkeit vermieden wird, viel zu handhaben der Low-Level-Details für seine Verwendung.

Alle sicherheitsrelevanten Bibliotheksabhängigkeiten werden von Maven mit Hilfe von Spring Boot gehandhabt, das die einzige Komponente ist, die eine explizite Version in der Konfigurationsdatei pom.xml von Maven erfordert (dh Bibliotheksversionen werden automatisch von Maven abgeleitet, indem die aktuellste ausgewählt wird Version, die mit der eingefügten Spring Boot-Version kompatibel ist).

Nachfolgend finden Sie den Auszug aus der Konfigurationsdatei pom.xml von maven, der die Abhängigkeiten in Bezug auf die Spring Boot-Sicherheit enthält:

 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> <version>2.1.0.RELEASE</version> </dependency>

Die App fungiert sowohl als OAuth2-Autorisierungsserver/Ressourcenbesitzer als auch als Ressourcenserver.

Die geschützten Ressourcen (als Ressourcenserver) werden unter /api/ Pfad veröffentlicht, während der Authentifizierungspfad (als Ressourceneigentümer/Autorisierungsserver) gemäß der vorgeschlagenen Standardeinstellung /oauth/token zugeordnet wird.

Aufbau der App:

  • security mit Sicherheitskonfiguration
  • Fehlerpaket, das die errors enthält
  • users , glee -Pakete für REST-Ressourcen, einschließlich Modell, Repository und Controller

Die nächsten Absätze behandeln die Konfiguration für jede der drei oben erwähnten OAuth2-Rollen. Die zugehörigen Klassen befinden sich im security :

  • OAuthConfiguration , erweitert AuthorizationServerConfigurerAdapter
  • ResourceServerConfiguration , Erweiterung von ResourceServerConfigurerAdapter
  • ServerSecurityConfig , Erweiterung WebSecurityConfigurerAdapter
  • UserService , implementiert UserDetailsService

Setup für Ressourceneigentümer und Autorisierungsserver

Das Verhalten des Autorisierungsservers wird durch das Vorhandensein der Annotation @EnableAuthorizationServer . Seine Konfiguration wird mit derjenigen zusammengeführt, die sich auf das Verhalten des Ressourcenbesitzers bezieht, und beide sind in der Klasse AuthorizationServerConfigurerAdapter enthalten.

Die hier angewendeten Konfigurationen beziehen sich auf:

  • Clientzugriff (mit ClientDetailsServiceConfigurer )
    • Auswahl der Verwendung eines In-Memory- oder JDBC-basierten Speichers für Client-Details mit inMemory oder jdbc Methoden
    • Die grundlegende Authentifizierung des Clients mit den Attributen clientId und clientSecret (codiert mit der ausgewählten PasswordEncoder -Bean).
    • Gültigkeitszeit für Zugriffs- und Aktualisierungstoken mit den Attributen accessTokenValiditySeconds und refreshTokenValiditySeconds
    • Gewährungstypen, die mit dem Attribut „ authorizedGrantTypes “ zulässig sind
    • Definiert Zugriffsbereiche mit der Methode scopes
    • Identifizieren Sie die zugänglichen Ressourcen des Kunden
  • Autorisierungsserver-Endpunkt (unter Verwendung von AuthorizationServerEndpointsConfigurer )
    • Definieren Sie die Verwendung eines JWT-Tokens mit accessTokenConverter
    • Definieren Sie die Verwendung einer UserDetailsService und AuthenticationManager -Schnittstelle zur Durchführung der Authentifizierung (als Ressourceneigentümer).
 package net.reliqs.gleeometer.security; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; @Configuration @EnableAuthorizationServer public class OAuthConfiguration extends AuthorizationServerConfigurerAdapter { private final AuthenticationManager authenticationManager; private final PasswordEncoder passwordEncoder; private final UserDetailsService userService; @Value("${jwt.clientId:glee-o-meter}") private String clientId; @Value("${jwt.client-secret:secret}") private String clientSecret; @Value("${jwt.signing-key:123}") private String jwtSigningKey; @Value("${jwt.accessTokenValidititySeconds:43200}") // 12 hours private int accessTokenValiditySeconds; @Value("${jwt.authorizedGrantTypes:password,authorization_code,refresh_token}") private String[] authorizedGrantTypes; @Value("${jwt.refreshTokenValiditySeconds:2592000}") // 30 days private int refreshTokenValiditySeconds; public OAuthConfiguration(AuthenticationManager authenticationManager, PasswordEncoder passwordEncoder, UserDetailsService userService) { this.authenticationManager = authenticationManager; this.passwordEncoder = passwordEncoder; this.userService = userService; } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient(clientId) .secret(passwordEncoder.encode(clientSecret)) .accessTokenValiditySeconds(accessTokenValiditySeconds) .refreshTokenValiditySeconds(refreshTokenValiditySeconds) .authorizedGrantTypes(authorizedGrantTypes) .scopes("read", "write") .resourceIds("api"); } @Override public void configure(final AuthorizationServerEndpointsConfigurer endpoints) { endpoints .accessTokenConverter(accessTokenConverter()) .userDetailsService(userService) .authenticationManager(authenticationManager); } @Bean JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); return converter; } }

Im nächsten Abschnitt wird die auf den Ressourcenserver anzuwendende Konfiguration beschrieben.

Setup für Ressourcenserver

Das Verhalten des Ressourcenservers wird durch die Verwendung der Annotation @EnableResourceServer und seine Konfiguration ist in der Klasse ResourceServerConfiguration enthalten.

Die einzige erforderliche Konfiguration hier ist die Definition der Ressourcenidentifikation, um den Zugriff des Clients abzugleichen, der in der vorherigen Klasse definiert wurde.

 package net.reliqs.gleeometer.security; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; @Configuration @EnableResourceServer public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Override public void configure(ResourceServerSecurityConfigurer resources) { resources.resourceId("api"); } }

Beim letzten Konfigurationselement geht es um die Definition der Sicherheit von Webanwendungen.

Web-Sicherheits-Setup

Die Spring-Websicherheitskonfiguration ist in der Klasse ServerSecurityConfig enthalten, die durch die Verwendung der Annotation @EnableWebSecurity wird. @EnableGlobalMethodSecurity ermöglicht die Angabe der Sicherheit auf Methodenebene. Sein Attribut proxyTargetClass wird gesetzt, damit dies für die Methoden von RestController funktioniert, da Controller normalerweise Klassen sind, die keine Schnittstellen implementieren.

Es definiert Folgendes:

  • Der zu verwendende Authentifizierungsanbieter, der den Bean authenticationProvider definiert
  • Der zu verwendende Passwort-Encoder, der die Bean passwordEncoder definiert
  • Die Authentifizierungs-Manager-Bean
  • Die Sicherheitskonfiguration für die veröffentlichten Pfade mit HttpSecurity
  • Verwendung eines benutzerdefinierten AuthenticationEntryPoint , um Fehlermeldungen außerhalb des standardmäßigen Spring-REST-Fehlerhandlers ResponseEntityExceptionHandler zu behandeln
 package net.reliqs.gleeometer.security; import net.reliqs.gleeometer.errors.CustomAccessDeniedHandler; import net.reliqs.gleeometer.errors.CustomAuthenticationEntryPoint; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true) public class ServerSecurityConfig extends WebSecurityConfigurerAdapter { private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint; private final UserDetailsService userDetailsService; public ServerSecurityConfig(CustomAuthenticationEntryPoint customAuthenticationEntryPoint, @Qualifier("userService") UserDetailsService userDetailsService) { this.customAuthenticationEntryPoint = customAuthenticationEntryPoint; this.userDetailsService = userDetailsService; } @Bean public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setPasswordEncoder(passwordEncoder()); provider.setUserDetailsService(userDetailsService); return provider; } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity http) throws Exception { http .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/api/signin/**").permitAll() .antMatchers("/api/glee/**").hasAnyAuthority("ADMIN", "USER") .antMatchers("/api/users/**").hasAuthority("ADMIN") .antMatchers("/api/**").authenticated() .anyRequest().authenticated() .and().exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint).accessDeniedHandler(new CustomAccessDeniedHandler()); } }

Im folgenden Codeauszug geht es um die Implementierung der UserDetailsService Schnittstelle, um die Authentifizierung des Ressourcenbesitzers bereitzustellen.

 package net.reliqs.gleeometer.security; import net.reliqs.gleeometer.users.User; import net.reliqs.gleeometer.users.UserRepository; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; 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.Service; @Service public class UserService implements UserDetailsService { private final UserRepository repository; public UserService(UserRepository repository) { this.repository = repository; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = repository.findByEmail(username).orElseThrow(() -> new RuntimeException("User not found: " + username)); GrantedAuthority authority = new SimpleGrantedAuthority(user.getRole().name()); return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), Arrays.asList(authority)); } }

Im nächsten Abschnitt geht es um die Beschreibung einer REST-Controller-Implementierung, um zu sehen, wie Sicherheitsbeschränkungen abgebildet werden.

REST-Controller

Innerhalb des REST-Controllers finden wir zwei Möglichkeiten, die Zugriffskontrolle für jede Ressourcenmethode anzuwenden:

  • Verwenden einer Instanz von OAuth2Authentication , die von Spring als Parameter übergeben wird
  • Verwenden @PreAuthorize oder @PostAuthorize Anmerkungen
 package net.reliqs.gleeometer.users; import lombok.extern.slf4j.Slf4j; import net.reliqs.gleeometer.errors.EntityNotFoundException; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.ConstraintViolationException; import javax.validation.Valid; import javax.validation.constraints.Size; import java.util.HashSet; @RestController @RequestMapping("/api/users") @Slf4j @Validated class UserController { private final UserRepository repository; private final PasswordEncoder passwordEncoder; UserController(UserRepository repository, PasswordEncoder passwordEncoder) { this.repository = repository; this.passwordEncoder = passwordEncoder; } @GetMapping Page<User> all(@PageableDefault(size = Integer.MAX_VALUE) Pageable pageable, OAuth2Authentication authentication) { String auth = (String) authentication.getUserAuthentication().getPrincipal(); String role = authentication.getAuthorities().iterator().next().getAuthority(); if (role.equals(User.Role.USER.name())) { return repository.findAllByEmail(auth, pageable); } return repository.findAll(pageable); } @GetMapping("/search") Page<User> search(@RequestParam String email, Pageable pageable, OAuth2Authentication authentication) { String auth = (String) authentication.getUserAuthentication().getPrincipal(); String role = authentication.getAuthorities().iterator().next().getAuthority(); if (role.equals(User.Role.USER.name())) { return repository.findAllByEmailContainsAndEmail(email, auth, pageable); } return repository.findByEmailContains(email, pageable); } @GetMapping("/findByEmail") @PreAuthorize("!hasAuthority('USER') || (authentication.principal == #email)") User findByEmail(@RequestParam String email, OAuth2Authentication authentication) { return repository.findByEmail(email).orElseThrow(() -> new EntityNotFoundException(User.class, "email", email)); } @GetMapping("/{id}") @PostAuthorize("!hasAuthority('USER') || (returnObject != null && returnObject.email == authentication.principal)") User one(@PathVariable Long id) { return repository.findById(id).orElseThrow(() -> new EntityNotFoundException(User.class, "id", id.toString())); } @PutMapping("/{id}") @PreAuthorize("!hasAuthority('USER') || (authentication.principal == @userRepository.findById(#id).orElse(new net.reliqs.gleeometer.users.User()).email)") void update(@PathVariable Long id, @Valid @RequestBody User res) { User u = repository.findById(id).orElseThrow(() -> new EntityNotFoundException(User.class, "id", id.toString())); res.setPassword(u.getPassword()); res.setGlee(u.getGlee()); repository.save(res); } @PostMapping @PreAuthorize("!hasAuthority('USER')") User create(@Valid @RequestBody User res) { return repository.save(res); } @DeleteMapping("/{id}") @PreAuthorize("!hasAuthority('USER')") void delete(@PathVariable Long id) { if (repository.existsById(id)) { repository.deleteById(id); } else { throw new EntityNotFoundException(User.class, "id", id.toString()); } } @PutMapping("/{id}/changePassword") @PreAuthorize("!hasAuthority('USER') || (#oldPassword != null && !#oldPassword.isEmpty() && authentication.principal == @userRepository.findById(#id).orElse(new net.reliqs.gleeometer.users.User()).email)") void changePassword(@PathVariable Long id, @RequestParam(required = false) String oldPassword, @Valid @Size(min = 3) @RequestParam String newPassword) { User user = repository.findById(id).orElseThrow(() -> new EntityNotFoundException(User.class, "id", id.toString())); if (oldPassword == null || oldPassword.isEmpty() || passwordEncoder.matches(oldPassword, user.getPassword())) { user.setPassword(passwordEncoder.encode(newPassword)); repository.save(user); } else { throw new ConstraintViolationException("old password doesn't match", new HashSet<>()); } } }

Fazit

Spring Security und Spring Boot ermöglichen es, auf fast deklarative Weise schnell einen vollständigen OAuth2-Autorisierungs-/Authentifizierungsserver einzurichten. Die Einrichtung kann weiter verkürzt werden, indem die Eigenschaften des OAuth2-Clients direkt aus der Datei „ application.properties/yml “ konfiguriert werden, wie in diesem Tutorial erläutert.

Der gesamte Quellcode ist in diesem GitHub-Repository verfügbar: spring-glee-o-meter. Ein Angular-Client, der die veröffentlichten Ressourcen nutzt, findet sich in diesem GitHub-Repository: glee-o-meter.