Security mit OAuth2/OIDC
Von vertrauten Headern zu geprüften JWT-Scopes und Method Security.
Workshop-Slice
Dieses Lab zeigt einen konkreten Enterprise-Fehler mit Vorher/Nachher-Code. Die Beispiele sind bewusst fachlich benannt, damit du Architekturentscheidungen und nicht nur Syntax übst.
Lernziel
Du schützt einen Enterprise-Endpunkt mit geprüfter Identität, Rollen/Scopes und testbarer Autorisierung.
Ausgangslage
Ein interner Service vertraut auf Header wie X-User-Id und X-Role. In einer echten Plattform kann das bei falscher Gateway-Konfiguration zu Privilege Escalation führen.
Vorher: problematischer Code
Die API glaubt Headern, die jeder Client theoretisch senden kann.
@PostMapping("/orders/{id}/cancel")
void cancel(@PathVariable String id,
@RequestHeader("X-User-Id") String userId,
@RequestHeader("X-Role") String role) {
if (!"ADMIN".equals(role)) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
}
orderService.cancel(id, userId);
}
Analyse: Was ist daran schlecht?
- Header sind kein Beweis für Identität.
- Autorisierung ist im Controller hart codiert.
- Scopes/Rollen sind nicht zentral dokumentiert.
- Tests prüfen oft nur Happy Path.
Nachher: bessere Lösung
Die Zielversion nutzt JWT-Authentifizierung, Scope-Prüfung und Method Security am Use Case.
@Configuration
@EnableMethodSecurity
class SecurityConfig {
@Bean
SecurityFilterChain api(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.POST, "/orders/*/cancel").authenticated()
.anyRequest().denyAll())
.oauth2ResourceServer(oauth -> oauth.jwt(Customizer.withDefaults()))
.build();
}
}
@Service
class CancelOrderUseCase {
// Pattern: Policy Enforcement at Application Boundary
// Zweck: Fachliche Operation ist unabhängig vom Controller geschützt.
@PreAuthorize("hasAuthority('SCOPE_order.cancel')")
public void cancel(OrderId id, UserId requestedBy) {
Order order = orders.get(id);
order.cancel(requestedBy);
}
}
api: order-service
scopes:
order.read: Bestellungen lesen
order.create: Bestellungen anlegen
order.cancel: Bestellungen stornieren
roles:
support-agent: [order.read]
order-manager: [order.read, order.create, order.cancel]
Test / Prüfnachweis
Dieser Abschnitt zeigt, wie du die Verbesserung nachweist. Es ist bewusst kein reiner Happy-Path-Test, sondern prüft ein Risiko aus dem Vorher-Teil.
@Test
void cancelRequiresCancelScope() throws Exception {
mvc.perform(post("/orders/O-55/cancel")
.with(jwt().authorities(new SimpleGrantedAuthority("SCOPE_order.read"))))
.andExpect(status().isForbidden());
mvc.perform(post("/orders/O-55/cancel")
.with(jwt().authorities(new SimpleGrantedAuthority("SCOPE_order.cancel"))))
.andExpect(status().isNoContent());
}
Typische Fehler
- Gateway-Header ungeprüft als Identität verwenden.
- Rollen in jedem Controller anders benennen.
- Nur UI verstecken, API aber nicht schützen.
- 403/401 nicht unterscheiden.
Deep-Learning-Bezug
Die Links führen zum ausführlichen Inhalt; die Deep-Learning-Seite bleibt nur die Lernlandkarte und kopiert den Inhalt nicht doppelt.
Prüfcheckliste
- Keine Autorisierung über ungeprüfte Client-Header.
- Scope-Vertrag ist dokumentiert.
- Negativtest für fehlende Berechtigung vorhanden.
- Use Case ist zusätzlich geschützt.