Você está em: Página Inicial> Imprensa> Notícias

Você está em: Página Inicial> Imprensa> Notícias

*1. Introdução *

Na maioria das vezes, ao proteger um aplicativo Web Spring ou uma API REST, as ferramentas fornecidas pelo Spring Security são mais do que suficientes, mas às vezes procuramos um comportamento mais específico.

Neste tutorial, escreveremos um AccessDecisionVoter personalizado e mostraremos como ele pode ser usado para abstrair a lógica de autorização de um aplicativo da Web e separá-lo da lógica de negócios do aplicativo.

2.* Cenário *

Para demonstrar como o AccessDecisionVoter funciona, implementaremos um cenário com dois tipos de usuário, USER e ADMIN, _ nos quais um _USER pode acessar o sistema apenas em minutos pares, enquanto um ADMIN sempre terá acesso.

===* 3. AccessDecisionVoter Implementações *

Primeiro, descreveremos algumas das implementações fornecidas pelo Spring que participarão ao lado do nosso eleitor personalizado na decisão final sobre a autorização. Em seguida, veremos como implementar um eleitor personalizado.

====* 3.1 As implementações AccessDecisionVoter padrão *

O Spring Security fornece várias implementações AccessDecisionVoter. Usaremos alguns deles como parte de nossa solução de segurança aqui.

Vamos dar uma olhada em como e quando essas implementações de eleitores padrão votam.

O AuthenticatedVoter votará com base no nível de autenticação do objeto Authentication - procurando especificamente por um pricipal totalmente autenticado, um autenticado com lembrete de mim ou, finalmente, anônimo.

O RoleVoter vota se algum dos atributos de configuração começa com a String _ "ROLE " . Nesse caso, procurará a função na lista GrantedAuthority do objeto Authentication.

O WebExpressionVoter nos permite usar o SpEL (Spring Expression Language) para autorizar as solicitações usando a anotação _ @ PreAuthorize_.

Por exemplo, se estivermos usando a configuração Java:

@Override
protected void configure(final HttpSecurity http) throws Exception {
    ...
    .antMatchers("/").hasAnyAuthority("ROLE_USER")
    ...
}

Ou usando uma configuração XML - podemos usar o SpEL dentro de uma tag intercept-url, na tag http:

<http use-expressions="true">
    <intercept-url pattern="/"
      access="hasAuthority('ROLE_USER')"/>
    ...
</http>

====* 3.2 Implementação personalizada AccessDecisionVoter *

Agora vamos criar um eleitor personalizado - implementando a interface AccessDecisionVoter:

public class MinuteBasedVoter implements AccessDecisionVoter {
   ...
}

O primeiro dos três métodos que devemos fornecer é o método vote. O método vote é a parte mais importante do eleitor personalizado e é para onde nossa lógica de autorização vai.

O método vote pode retornar três valores possíveis:

  • ACCESS_GRANTED - o eleitor dá uma resposta afirmativa

  • ACCESS_DENIED - o eleitor responde negativamente *ACCESS_ABSTAIN - o eleitor se abstém de votar

Vamos agora implementar o método vote:

@Override
public int vote(
  Authentication authentication, Object object, Collection collection) {
    return authentication.getAuthorities().stream()
      .map(GrantedAuthority::getAuthority)
      .filter(r -> "ROLE_USER".equals(r)
        && LocalDateTime.now().getMinute() % 2 != 0)
      .findAny()
      .map(s -> ACCESS_DENIED)
      .orElseGet(() -> ACCESS_ABSTAIN);
}

No nosso método vote, verificamos se a solicitação vem de um USER. Nesse caso, retornamos ACCESS_GRANTED se for um minuto par, caso contrário, retornamos ACCESS_DENIED. Se a solicitação não vier de um USER, _ nos absteremos da votação e retornaremos _ACCESS_ABSTAIN.

O segundo método retorna se o eleitor suporta um atributo de configuração específico. Em nosso exemplo, o eleitor não precisa de nenhum atributo de configuração personalizado, portanto, retornamos true:

@Override
public boolean supports(ConfigAttribute attribute) {
    return true;
}

O terceiro método retorna se o eleitor pode votar no tipo de objeto protegido ou não. Como nosso eleitor não está preocupado com o tipo de objeto protegido, retornamos true:

@Override
public boolean supports(Class clazz) {
    return true;
}

===* 4. O AccessDecisionManager *

A decisão final de autorização é tratada pelo AccessDecisionManager.

O AbstractAccessDecisionManager contém uma lista de AccessDecisionVoters - responsáveis ​​por emitir seus votos independentemente um do outro.

Existem três implementações para processar os votos para cobrir os casos de uso mais comuns:

  • AffirmativeBased - concede acesso se algum dos AccessDecisionVoter retornar um voto afirmativo

  • ConsensusBased - concede acesso se houver mais votos afirmativos do que negativos (ignorando usuários que se abstêm) *UnanimousBased - concede acesso se cada eleitor se abstiver ou retornar um voto afirmativo

Obviamente, você pode implementar seu próprio AccessDecisionManager com sua lógica de tomada de decisão personalizada.

===* 5. Configuração *

Nesta parte do tutorial, examinaremos os métodos baseados em Java e XML para configurar nosso AccessDecisionVoter personalizado com um AccessDecisionManager.

====* 5.1. Configuração Java *

Vamos criar uma classe de configuração para o Spring Web Security:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
}

E vamos definir um bean AccessDecisionManager que usa um gerenciador UnanimousBased com nossa lista personalizada de eleitores:

@Bean
public AccessDecisionManager accessDecisionManager() {
    List<AccessDecisionVoter<? extends Object>> decisionVoters
      = Arrays.asList(
        new WebExpressionVoter(),
        new RoleVoter(),
        new AuthenticatedVoter(),
        new MinuteBasedVoter());
    return new UnanimousBased(decisionVoters);
}

Por fim, vamos configurar o Spring Security para usar o bean definido anteriormente como o AccessDecisionManager padrão:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
    ...
    .anyRequest()
    .authenticated()
    .accessDecisionManager(accessDecisionManager());
}

====* 5.2 Configuração XML *

Se estiver usando a configuração XML, será necessário modificar o arquivo spring-security.xml (ou o arquivo que contém suas configurações de segurança).

Primeiro, você precisará modificar a tag _ <http> _:

<http access-decision-manager-ref="accessDecisionManager">
  <intercept-url
    pattern="/**"
    access="hasAnyRole('ROLE_ADMIN', 'ROLE_USER')"/>
  ...
</http>

Em seguida, adicione um bean para o eleitor personalizado:

<beans:bean
  id="minuteBasedVoter"
  class="org..voter.MinuteBasedVoter"/>

Em seguida, adicione um bean para o AccessDecisionManager:

<beans:bean
  id="accessDecisionManager"
  class="org.springframework.security.access.vote.UnanimousBased">
    <beans:constructor-arg>
        <beans:list>
            <beans:bean class=
              "org.springframework.security.web.access.expression.WebExpressionVoter"/>
            <beans:bean class=
              "org.springframework.security.access.vote.AuthenticatedVoter"/>
            <beans:bean class=
              "org.springframework.security.access.vote.RoleVoter"/>
            <beans:bean class=
              "org..voter.MinuteBasedVoter"/>
        </beans:list>
    </beans:constructor-arg>
</beans:bean>

Aqui está um exemplo de tag <authentication-manager> _ que suporta nosso cenário:

<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="user" password="pass" authorities="ROLE_USER"/>
            <user name="admin" password="pass" authorities="ROLE_ADMIN"/>
        </user-service>
    </authentication-provider>
</authentication-manager>

Se você estiver usando uma combinação de configuração Java e XML, poderá importar o XML para uma classe de configuração:

@Configuration
@ImportResource({"classpath:spring-security.xml"})
public class XmlSecurityConfig {
    public XmlSecurityConfig() {
        super();
    }
}

6. Conclusão

Neste tutorial, vimos uma maneira de personalizar a segurança de um aplicativo da Web Spring usando AccessDecisionVoters. Vimos alguns eleitores fornecidos pela Spring Security que contribuíram para nossa solução. Em seguida, discutimos como implementar um AccessDecisionVoter personalizado.

Em seguida, discutimos como o AccessDecisionManager toma a decisão final de autorização e mostramos como usar as implementações fornecidas pelo Spring para tomar essa decisão depois que todos os eleitores votaram.

Em seguida, configuramos uma lista de AccessDecisionVoters com um AccessDecisionManager por meio de Java e XML.

A implementação pode ser encontrada no Projeto Github.

Quando o projeto é executado localmente, a página de login pode ser acessada em:

http://localhost:8080/spring-security-custom-permissions/login

As credenciais para o USER são "usuário" e "aprovação", e as credenciais para o ADMIN são "admin" e "aprovação".