Extraindo Principal e Autoridades usando o Spring Security OAuth

Extraindo Principal e Autoridades usando o Spring Security OAuth

1. Visão geral

Neste tutorial, ilustraremos como criar um aplicativo que delega a autenticação do usuário a um terceiro, bem como a um servidor de autorização personalizado, usando Spring Boot e Spring Security OAuth.

Além disso,we’ll demonstrate how to extract both Principal and Authorities using Spring’s PrincipalExtractor and AuthoritiesExtractor interfaces.

Para uma introdução ao Spring Security OAuth2, consulte os artigosthese.

2. Dependências do Maven

Para começar, precisamos adicionar a dependênciaspring-security-oauth2-autoconfigure ao nossopom.xml:


    org.springframework.security.oauth.boot
    spring-security-oauth2-autoconfigure
    2.0.1.RELEASE

3. Autenticação OAuth usando o Github

A seguir, vamos criar a configuração de segurança do nosso aplicativo:

@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();
    }
}

Resumindo, estamos dizendo que qualquer pessoa pode acessar o endpoint/login e que todos os outros endpoints exigirão autenticação do usuário.

Também anotamos nossa classe de configuração com@EnableOAuthSso, que converte nosso aplicativo em um cliente OAuth e cria os componentes necessários para que ele se comporte como tal.

Enquanto o Spring cria a maioria dos componentes para nós por padrão, ainda precisamos configurar algumas propriedades:

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

Em vez de lidar com o gerenciamento da conta do usuário, estamos delegando-o a um terceiro - neste caso, Github - permitindo que nos concentremos na lógica de nosso aplicativo.

4. Extração de Principal e Autoridades

Ao atuar como um cliente OAuth e autenticar usuários por meio de terceiros, há três etapas que precisamos considerar:

  1. Autenticação de usuário - o usuário se autentica com terceiros

  2. Autorização do usuário - segue a autenticação, é quando o usuário permite que nosso aplicativo execute certas operações em seu nome; é aqui que entrascopes

  3. Buscar dados do usuário - use o token OAuth que obtivemos para recuperar os dados do usuário

Depois de recuperar os dados do usuário,Spring is able to automatically create the user’s Principal and Authorities.

Embora isso possa ser aceitável, na maioria das vezes nos encontramos em um cenário em que queremos ter total controle sobre eles.

Para fazer isso,Spring gives us two interfaces we can use to override its default behavior:

  • PrincipalExtractor - interface que podemos usar para fornecer nossa lógica personalizada para extrair oPrincipal

  • AuthoritiesExtractor - semelhante aPrincipalExtractor, mas é usado para personalizar a extração deAuthorities

Por padrão,Spring provides two components – FixedPrincipalExtractor and FixedAuthoritiesExtractor  que implementam essas interfaces e têm uma estratégia pré-definida para criá-las para nós.

4.1. Personalizando a autenticação do Github

Em nosso caso, estamos cientes de como os dados do usuário do Github parecemlikee o que podemos usar para personalizá-los de acordo com nossas necessidades.

Como tal, para substituir os componentes padrão do Spring, só precisamos criar doisBeans que também implementam essas interfaces.

Para osPrincipal do nosso aplicativo, vamos simplesmente usar o nome de usuário Github do usuário:

public class GithubPrincipalExtractor
  implements PrincipalExtractor {

    @Override
    public Object extractPrincipal(Map map) {
        return map.get("login");
    }
}

Dependendo da assinatura do Github de nosso usuário - gratuita ou não - daremos a eles uma autoridadeGITHUB_USER_SUBSCRIBED ouGITHUB_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;
    }
}

Em seguida, também precisamos criar beans usando estas classes:

@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // ...

    @Bean
    public PrincipalExtractor githubPrincipalExtractor() {
        return new GithubPrincipalExtractor();
    }

    @Bean
    public AuthoritiesExtractor githubAuthoritiesExtractor() {
        return new GithubAuthoritiesExtractor();
    }
}

4.2. Usando um servidor de autorização personalizado

Também podemos usar nosso próprio servidor de autorização para nossos usuários - em vez de depender de terceiros.

Apesar do servidor de autorização que decidimos usar, os componentes que precisamos personalizarPrincipaleAuthorities permanecem os mesmos: aPrincipalExtractore umAuthoritiesExtractor.

Precisamos apenasbe aware of the data returned by the user-info-uri endpointe usá-lo como acharmos adequado.

Vamos mudar nosso aplicativo para autenticar nossos usuários usando o servidor de autorização descrito no artigothis:

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

Agora que estamos apontando para nosso servidor de autorização, precisamos criar os dois extratores; neste caso, nossoPrincipalExtractor vai extrairPrincipal deMap usando a chavename:

public class examplePrincipalExtractor
  implements PrincipalExtractor {

    @Override
    public Object extractPrincipal(Map map) {
        return map.get("name");
    }
}

Quanto às autoridades, nosso servidor de autorização já as está colocando em seus dados deuser-info-uri.

Como tal, vamos extraí-los e enriquecê-los:

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);
    }
}

Então, vamos adicionar os grãos à nossa classeSecurityConfig:

@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // ...

    @Bean
    public PrincipalExtractor examplePrincipalExtractor() {
        return new examplePrincipalExtractor();
    }

    @Bean
    public AuthoritiesExtractor exampleAuthoritiesExtractor() {
        return new exampleAuthoritiesExtractor();
    }
}

5. Conclusão

Neste artigo, implementamos um aplicativo que delega a autenticação do usuário a um terceiro, bem como a um servidor de autorização personalizado, e demonstramos como personalizarPrincipaleAuthorities.

Como de costume, a implementação deste exemplo pode ser encontradaover on Github.

Ao executar localmente, você pode executar e testar o aplicativo emlocalhost:8082