Entziehen von Principal und Authorities mit Spring Security OAuth

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:

  1. Benutzerauthentifizierung - Der Benutzer authentifiziert sich beim Dritten

  2. Benutzerautorisierung - folgt der Authentifizierung, wenn der Benutzer unserer Anwendung erlaubt, bestimmte Vorgänge in ihrem Namen auszuführen. Hier kommenscopesins Spiel

  3. 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