Ich habe zwei separate Spring Boot-Anwendungen, eine als OAuth 2-Autorisierungsserver und die andere als Ressourcenserver. Ich verwende die RemoteTokenServices
von Spring auf meinem Ressourcenserver, um Token vom Autorisierungsserver zu überprüfen. Jetzt versuche ich, geschützten Controller-Code in meiner Ressourcenserver-Anwendung zu definieren, bin mir jedoch nicht sicher, wie ich die Klasse UserDetails
dem Authentifizierungsprinzip zuordnen kann, das über den OAuth 2-Mechanismus bereitgestellt wird.
Ich habe meinen Berechtigungsserver mit einer benutzerdefinierten TokenEnhancer
eingerichtet, die dem Token weitere Details hinzufügt, sodass /oauth/check_token?token=<token>
mit benutzerdefinierten Feldern zurückgegeben wird, die ich meinen Ressourcenserver-Controllern zuordnen möchte.
In einer eher monolithischen Konfiguration, in der der Autorisierungsserver auch der Ressourcenserver ist, kann ich Controllermethoden definieren, die den authentifizierten Principal folgendermaßen verwenden:
//User implements UserDetails
public Map<String, Object> getResource(@AuthenticationPrincipal User user) {
//code that uses the user object
}
Dies scheint jedoch in einem verteilten Ansatz nicht so einfach zu funktionieren. Die Zuordnung schlägt fehl und der Parameter user
ist am Ende ein Nullobjekt. Ich habe versucht, den folgenden Ansatz zu verwenden:
public Map<String, Object> getResource(Authentication authentication) {
//code that uses the authentication object
}
Der obige Code ordnet die Authentifizierungsdetails zwar erfolgreich zu, bietet mir jedoch keine Möglichkeit, direkt auf die benutzerdefinierten Felder zuzugreifen, die ich über die zuvor erwähnte TokenEnhancer
festgelegt habe. Ich kann in den Spring-Dokumenten anscheinend nichts dazu finden.
Um das Problem zu lösen, lassen Sie mich zunächst ein wenig architektonischen Hintergrund betrachten. Das UserDetails
-Objekt, das automatisch durch den @AuthenticationPrincipal
zugeordnet wird, stammt aus dem principal
-Feld des aktiven Authentication
-Objekts. Ein Resource Server-Controller hat Zugriff auf ein OAuth2Authencation
-Objekt, bei dem es sich um eine spezialisierte Instanz von Authentication
für das Spring OAuth2-Sicherheitsframework handelt, indem es einfach als Teil der Methodenparameter deklariert wird.
public void controllerMethod(OAuth2Authentication authentication) {
//controller definition
}
Wenn Sie dies wissen, wird das Problem nun verschoben, um sicherzustellen, dass die getPrincipal()
-Methode im Authentication
-Objekt eine Instanz meiner benutzerdefinierten UserDetails
-Klasse ist. Die Variable RemoteTokenServices
, die ich in der Ressourcenserveranwendung verwende, verwendet eine Instanz von AccessTokenConverter
, um die vom Berechtigungsserver gesendeten Token-Details zu interpretieren. Standardmäßig wird DefaultAccessTokenConverter
verwendet, wodurch der Authentifizierungsprinzipal nur als Benutzername festgelegt wird. Dies ist eine String
. Dieser Konverter verwendet UserAuthenticationConverter
, um die vom Autorisierungsserver kommenden Daten in eine Instanz von Authentication
zu konvertieren. Das musste ich anpassen:
DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
tokenConverter.setUserTokenConverter(new DefaultUserAuthenticationConverter() {
@Override
public Authentication extractAuthentication(Map<String, ?> map) {
Authentication authentication = super.extractAuthentication(map);
// User is my custom UserDetails class
User user = new User();
user.setSpecialKey(map.get("specialKey").toString());
return new UsernamePasswordAuthenticationToken(user,
authentication.getCredentials(), authentication.getAuthorities());
}
});
tokenServices.setAccessTokenConverter(tokenConverter);
Mit all diesen Einstellungen funktioniert der @AuthenticationPrincipal
-Mechanismus jetzt wie erwartet.
Haben Sie die Variable AuthenticationPrincipalArgumentResolver
wie folgt aktiviert?
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
Und Sie müssen die UserDetails
implementieren, um Ihren eigenen CustomerUser object
zurückzugeben. Anschließend können Sie die Annotation verwenden, um den Principal direkt abzurufen.