Extrahieren von Principal und Authorities mit Spring Security OAuth
1. Überblick
In diesem Lernprogramm wird veranschaulicht, wie eine Anwendung erstellt wird, die die Benutzerauthentifizierung mithilfe von Spring Boot und Spring Security OAuth an einen Drittanbieter sowie an einen benutzerdefinierten Autorisierungsserver delegiert.
Auchwe’ll demonstrate how to extract both Principal and Authorities using Spring’s PrincipalExtractor and AuthoritiesExtractor interfaces.
Eine Einführung in Spring Security OAuth2 finden Sie in den Artikeln vonthese.
2. Maven-Abhängigkeiten
Um zu beginnen, müssen wir die Abhängigkeit vonspring-security-oauth2-autoconfigurezu unserenpom.xmlhinzufügen:
org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure
2.0.1.RELEASE
3. OAuth-Authentifizierung mit Github
Als Nächstes erstellen wir die Sicherheitskonfiguration unserer Anwendung:
@Configuration
@EnableOAuth2Sso
public class SecurityConfig
extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http)
throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/login**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin().disable();
}
}
Kurz gesagt, wir sagen, dass jeder auf den Endpunkt von/loginzugreifen kann und dass alle anderen Endpunkte eine Benutzerauthentifizierung erfordern.
Wir haben unsere Konfigurationsklasse auch mit@EnableOAuthSso versehen, wodurch unsere Anwendung in einen OAuth-Client konvertiert und die erforderlichen Komponenten erstellt werden, damit sie sich als solche verhält.
Während Spring standardmäßig die meisten Komponenten für uns erstellt, müssen wir noch einige Eigenschaften konfigurieren:
security.oauth2.client.client-id=89a7c4facbb3434d599d
security.oauth2.client.client-secret=9b3b08e4a340bd20e866787e4645b54f73d74b6a
security.oauth2.client.access-token-uri=https://github.com/login/oauth/access_token
security.oauth2.client.user-authorization-uri=https://github.com/login/oauth/authorize
security.oauth2.client.scope=read:user,user:email
security.oauth2.resource.user-info-uri=https://api.github.com/user
Anstatt sich mit der Verwaltung von Benutzerkonten zu befassen, delegieren wir sie an einen Dritten - in diesem Fall an Github - und können uns so auf die Logik unserer Anwendung konzentrieren.
4. Auftraggeber und Behörden extrahieren
Wenn Sie als OAuth-Client fungieren und Benutzer durch Dritte authentifizieren, müssen Sie drei Schritte berücksichtigen:
-
Benutzerauthentifizierung - Der Benutzer authentifiziert sich beim Dritten
-
Benutzerautorisierung - folgt der Authentifizierung, wenn der Benutzer unserer Anwendung erlaubt, bestimmte Vorgänge in ihrem Namen auszuführen. Hier kommenscopesins Spiel
-
Benutzerdaten abrufen - Verwenden Sie das erhaltene OAuth-Token, um Benutzerdaten abzurufen
Sobald wir die Benutzerdaten abgerufen haben, werdenSpring is able to automatically create the user’s Principal and Authorities.
Auch wenn dies akzeptabel sein mag, befinden wir uns häufig in einem Szenario, in dem wir die vollständige Kontrolle über sie haben möchten.
DazuSpring gives us two interfaces we can use to override its default behavior:
-
PrincipalExtractor - Schnittstelle, über die wir unsere benutzerdefinierte Logik zum Extrahieren derPrincipal bereitstellen können
-
AuthoritiesExtractor - Ähnlich wiePrincipalExtractor, wird jedoch stattdessen zum Anpassen der Extraktion vonAuthoritiesverwendet
StandardmäßigSpring provides two components – FixedPrincipalExtractor and FixedAuthoritiesExtractor –, die diese Schnittstellen implementieren und eine vordefinierte Strategie haben, um sie für uns zu erstellen.
4.1. Anpassen der Github-Authentifizierung
In unserem Fall wissen wir, wie die Benutzerdaten von Github inlikeaussehen und wie wir sie an unsere Bedürfnisse anpassen können.
Um die Standardkomponenten von Spring zu überschreiben, müssen nur zweiBeanserstellt werden, die auch diese Schnittstellen implementieren.
Für diePrincipal unserer Anwendung verwenden wir einfach den Github-Benutzernamen des Benutzers:
public class GithubPrincipalExtractor
implements PrincipalExtractor {
@Override
public Object extractPrincipal(Map map) {
return map.get("login");
}
}
Abhängig vom kostenlosen oder anderweitigen Github-Abonnement unseres Benutzers erteilen wir ihm die BerechtigungGITHUB_USER_SUBSCRIBED oderGITHUB_USER_FREE:
public class GithubAuthoritiesExtractor
implements AuthoritiesExtractor {
List GITHUB_FREE_AUTHORITIES
= AuthorityUtils.commaSeparatedStringToAuthorityList(
"GITHUB_USER,GITHUB_USER_FREE");
List GITHUB_SUBSCRIBED_AUTHORITIES
= AuthorityUtils.commaSeparatedStringToAuthorityList(
"GITHUB_USER,GITHUB_USER_SUBSCRIBED");
@Override
public List extractAuthorities
(Map map) {
if (Objects.nonNull(map.get("plan"))) {
if (!((LinkedHashMap) map.get("plan"))
.get("name")
.equals("free")) {
return GITHUB_SUBSCRIBED_AUTHORITIES;
}
}
return GITHUB_FREE_AUTHORITIES;
}
}
Dann müssen wir auch Beans mit diesen Klassen erstellen:
@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// ...
@Bean
public PrincipalExtractor githubPrincipalExtractor() {
return new GithubPrincipalExtractor();
}
@Bean
public AuthoritiesExtractor githubAuthoritiesExtractor() {
return new GithubAuthoritiesExtractor();
}
}
4.2. Verwenden eines benutzerdefinierten Autorisierungsservers
Wir können für unsere Benutzer auch unseren eigenen Autorisierungsserver verwenden, anstatt uns auf Dritte zu verlassen.
Trotz des Autorisierungsservers, für den wir uns entscheiden, bleiben die Komponenten, die wir zum Anpassen vonPrincipal undAuthorities benötigen, gleich: aPrincipalExtractor undAuthoritiesExtractor.
Wir brauchen nurbe aware of the data returned by the user-info-uri endpoint und verwenden es, wie wir es für richtig halten.
Ändern Sie unsere Anwendung, um unsere Benutzer mithilfe des im Artikelthisbeschriebenen Autorisierungsservers zu authentifizieren:
security.oauth2.client.client-id=SampleClientId
security.oauth2.client.client-secret=secret
security.oauth2.client.access-token-uri=http://localhost:8081/auth/oauth/token
security.oauth2.client.user-authorization-uri=http://localhost:8081/auth/oauth/authorize
security.oauth2.resource.user-info-uri=http://localhost:8081/auth/user/me
Jetzt, da wir auf unseren Autorisierungsserver verweisen, müssen wir beide Extraktoren erstellen. In diesem Fall extrahieren unserePrincipalExtractor diePrincipal aus denMap mit dem Schlüsselname:
public class examplePrincipalExtractor
implements PrincipalExtractor {
@Override
public Object extractPrincipal(Map map) {
return map.get("name");
}
}
Behörden autorisieren sie bereits in denuser-info-uri-Daten.
Als solche werden wir sie extrahieren und bereichern:
public class exampleAuthoritiesExtractor
implements AuthoritiesExtractor {
@Override
public List extractAuthorities
(Map map) {
return AuthorityUtils
.commaSeparatedStringToAuthorityList(asAuthorities(map));
}
private String asAuthorities(Map map) {
List authorities = new ArrayList<>();
authorities.add("example_USER");
List> authz =
(List>) map.get("authorities");
for (LinkedHashMap entry : authz) {
authorities.add(entry.get("authority"));
}
return String.join(",", authorities);
}
}
Dann fügen wir die Bohnen unsererSecurityConfig-Klasse hinzu:
@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// ...
@Bean
public PrincipalExtractor examplePrincipalExtractor() {
return new examplePrincipalExtractor();
}
@Bean
public AuthoritiesExtractor exampleAuthoritiesExtractor() {
return new exampleAuthoritiesExtractor();
}
}
5. Fazit
In diesem Artikel haben wir eine Anwendung implementiert, die die Benutzerauthentifizierung an einen Drittanbieter sowie an einen benutzerdefinierten Autorisierungsserver delegiert, und gezeigt, wie sowohlPrincipal als auchAuthorities angepasst werden.
Wie üblich kann die Implementierung dieses Beispielsover on Github gefunden werden.
Wenn Sie lokal ausgeführt werden, können Sie die Anwendung beilocalhost:8082 ausführen und testen