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:
-
Autenticação de usuário - o usuário se autentica com terceiros
-
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
-
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