Diferença entre função e autoridade concedida na Spring Security

227

Existem conceitos e implementações no Spring Security, como a GrantedAuthorityinterface para obter uma autoridade para autorizar / controlar um acesso.

Gostaria que operações permitidas, como createSubUsers ou deleteAccounts , permitidas a um administrador (com função ROLE_ADMIN).

Estou ficando confuso com os tutoriais / demonstrações que vejo online. Tento conectar o que leio, mas acho que tratamos os dois de forma intercambiável.

Eu vejo hasRoleconsumindo uma GrantedAuthoritycorda? Definitivamente, estou fazendo errado na compreensão. O que são esses conceitualmente no Spring Security?

Como faço para armazenar a função de um usuário, separada das autoridades dessa função?

Também estou olhando para a org.springframework.security.core.userdetails.UserDetailsinterface que é usada no DAO referenciado pelo provedor de autenticação, que consome um User(note last GrantedAuthority):

public User(String username, 
            String password, 
            boolean enabled, 
            boolean accountNonExpired,
            boolean credentialsNonExpired, 
            boolean accountNonLocked, 
            Collection<? extends GrantedAuthority> authorities)

Ou existe outra maneira de diferenciar os outros dois? Ou não é suportado e temos que criar nossos próprios?

Chinmay
fonte

Respostas:

365

Pense em uma GrantedAuthority como sendo uma "permissão" ou um "direito". Essas "permissões" são (normalmente) expressas como seqüências de caracteres (com o getAuthority()método). Essas cadeias permitem identificar as permissões e permitir que seus eleitores decidam se concedem acesso a algo.

Você pode conceder diferentes GrantedAuthoritys (permissões) aos usuários colocando-os no contexto de segurança. Você normalmente faz isso implementando seu próprio UserDetailsService que retorna uma implementação de UserDetails que retorna as GrantedAuthorities necessárias.

Funções (como são usadas em muitos exemplos) são apenas "permissões" com uma convenção de nomenclatura que diz que uma função é uma GrantedAuthority que começa com o prefixo ROLE_. Não há mais nada. Uma função é apenas uma Autoridade Concedida - uma "permissão" - um "direito". Você vê muitos lugares na segurança da primavera em que a função com seu ROLE_prefixo é tratada especialmente como, por exemplo, no RoleVoter, onde o ROLE_prefixo é usado como padrão. Isso permite que você forneça os nomes das funções sem o ROLE_prefixo. Antes da segurança da Primavera 4, esse tratamento especial de "funções" não era seguido de maneira muito consistente e as autoridades e funções eram frequentemente tratadas da mesma maneira (como vocêhasAuthority()hasRole()) Com o Spring Security 4, o tratamento de funções é mais consistente e o código que lida com "funções" (como RoleVotera hasRoleexpressão etc.) sempre adiciona o ROLE_prefixo para você. Isso hasAuthority('ROLE_ADMIN')significa o mesmo que hasRole('ADMIN')porque o ROLE_prefixo é adicionado automaticamente. Consulte o guia de migração de segurança de mola 3 a 4 para obter mais informações.

Mas ainda assim: um papel é apenas uma autoridade com um ROLE_prefixo especial . Portanto, no Spring security 3 @PreAuthorize("hasRole('ROLE_XYZ')")é o mesmo que @PreAuthorize("hasAuthority('ROLE_XYZ')")e no Spring security 4 @PreAuthorize("hasRole('XYZ')")é o mesmo que @PreAuthorize("hasAuthority('ROLE_XYZ')").

Em relação ao seu caso de uso:

Os usuários têm funções e as funções podem executar determinadas operações.

Você pode acabar com GrantedAuthoritiesas funções às quais um usuário pertence e as operações que uma função pode executar. As GrantedAuthoritiesfunções para têm o prefixo ROLE_e as operações têm o prefixo OP_. Um exemplo para as autoridades de operação poderia ser OP_DELETE_ACCOUNT, OP_CREATE_USER, OP_RUN_BATCH_JOBetc. Os papéis podem ser ROLE_ADMIN, ROLE_USER, ROLE_OWNERetc.

Você pode acabar implementando suas entidades GrantedAuthoritycomo neste exemplo (pseudo-código):

@Entity
class Role implements GrantedAuthority {
    @Id
    private String id;

    @ManyToMany
    private final List<Operation> allowedOperations = new ArrayList<>();

    @Override
    public String getAuthority() {
        return id;
    }

    public Collection<GrantedAuthority> getAllowedOperations() {
        return allowedOperations;
    }
}

@Entity
class User {
    @Id
    private String id;

    @ManyToMany
    private final List<Role> roles = new ArrayList<>();

    public Collection<Role> getRoles() {
        return roles;
    }
}

@Entity
class Operation implements GrantedAuthority {
    @Id
    private String id;

    @Override
    public String getAuthority() {
        return id;
    }
}

Os IDs das funções e operações que você cria em seu banco de dados são a representação GrantedAuthority, por exemplo ROLE_ADMIN, OP_DELETE_ACCOUNTetc. Quando um usuário é autenticado, verifique se todas as GrantedAuthorities de todas as suas funções e as operações correspondentes são retornadas do UserDetails.getAuthorities () método.

Exemplo: A função admin com id ROLE_ADMINtem as operações OP_DELETE_ACCOUNT, OP_READ_ACCOUNT, OP_RUN_BATCH_JOBatribuído a ele. A função de usuário com id ROLE_USERpossui a operação OP_READ_ACCOUNT.

Se um administrador logs no contexto de segurança resultante terá os GrantedAuthorities: ROLE_ADMIN, OP_DELETE_ACCOUNT, OP_READ_ACCOUNT,OP_RUN_BATCH_JOB

Se um usuário fizer isso, ele vai ter: ROLE_USER,OP_READ_ACCOUNT

O UserDetailsService cuidaria de coletar todas as funções e todas as operações dessas funções e disponibilizá-las pelo método getAuthorities () na instância retornada de UserDetails.

James
fonte
2
Obrigado! Eu tenho procurado em todos os lugares por que "hasRole ('rolename')" não estava funcionando no Spring 4 -> A documentação deles não foi exatamente rápida de folhear. Apenas um rápido "encontrar e substituir" e estou de volta aos trilhos!
Jørgen Skår Fischer
12
Esta é uma ótima resposta. Uma coisa a esclarecer para explicar um pouco diferente: o hasRole('xyz')Spring Security 4 espera que você tenha o prefixo ROLE_, enquanto que hasAuthority('xyz')ele não espera o prefixo e avalia exatamente o que é passado. Eu usei essa solução, mas estava com problemas hasRole('OP_MY_PERMISSION')desde o prefixo ROLE_ era necessário. Em vez disso, eu deveria estar usando o hasAuthority('OP_MY_PERMISSION')desde que não tinha o prefixo.
randal4
@ James você pode olhar para esta questão stackoverflow.com/questions/41500031/...
saurabh Kumar
1
Em JSTL springframework.org/security/tags <sec:authorize access="hasRole('ADMIN')">é o mesmo que<sec:authorize access="hasRole('ROLE_ADMIN')">
Alex78191 22/17/17
1
Sim. OP_ é apenas um prefixo arbitrário. ROLE_ tem seu significado especial em algumas implementações de primavera.
James #
9

AFAIK GrantedAuthority e as funções são as mesmas na segurança da primavera. A string getAuthority () da GrantedAuthority é a função (conforme a implementação padrão SimpleGrantedAuthority).

Para o seu caso, você pode usar Funções Hierárquicas

<bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
    <constructor-arg ref="roleHierarchy" />
</bean>
<bean id="roleHierarchy"
        class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
    <property name="hierarchy">
        <value>
            ROLE_ADMIN > ROLE_createSubUsers
            ROLE_ADMIN > ROLE_deleteAccounts 
            ROLE_USER > ROLE_viewAccounts
        </value>
    </property>
</bean>

Não é o sol exato que você procura, mas espero que ajude

Editar : responda ao seu comentário

O papel é como uma permissão na segurança da primavera. o uso de intercept-url com hasRole fornece um controle muito refinado de qual operação é permitida para qual função / permissão.

A maneira como lidamos com nosso aplicativo é que definimos permissão (função) para cada operação (ou URL de descanso), por exemplo, view_account, delete_account, add_account etc. Em seguida, criamos perfis lógicos para cada usuário, como admin, guest_user, normal_user. Os perfis são apenas um agrupamento lógico de permissões, independente da segurança da mola. Quando um novo usuário é adicionado, um perfil é atribuído a ele (com todas as permissões permitidas). Agora, sempre que o usuário tenta executar alguma ação, a permissão / função dessa ação é verificada em relação às permissões concedidas pelo usuário.

Além disso, o RoleVoter defaultn usa o prefixo ROLE_; portanto, qualquer autoridade que comece com ROLE_ é considerada uma função, você pode alterar esse comportamento padrão usando um RolePrefix personalizado no votante da função e usando-o na segurança da primavera.

codificador
fonte
1
Obrigado pela ajuda. A hierarquia parece ser outra coisa. A maneira como estou pensando em fazer isso agora (se precisar usar o Spring Security) é armazenar a função e a autoridade na mesma lista e usar o predicado hasRole para executar as verificações para ambos. Pensando mais sobre isso - isso provavelmente foi deixado intencional pelos caras da Spring Security? Existe a possibilidade de usar o URL de interceptação que não seja a verificação usando hasRole na lista completa de Funções e privilégios / autoridades - ou outros aplicativos de autorização. Além disso, qual é a necessidade de prefixar ROLE_? Isso é convenção?
Chinmay
7

Outra maneira de entender a relação entre esses conceitos é interpretar um ROLE como um contêiner de autoridades.

As autoridades são permissões refinadas visando uma ação específica acoplada às vezes a escopo ou contexto de dados específicos. Por exemplo, Ler, Escrever, Gerenciar, pode representar vários níveis de permissões para um determinado escopo de informações.

Além disso, as autoridades são aplicadas profundamente no fluxo de processamento de uma solicitação, enquanto o ROLE é filtrado pelo filtro de solicitação antes de chegar ao Controlador. As melhores práticas prescrevem a implementação da aplicação das autoridades após o Controlador na camada de negócios.

Por outro lado, ROLES são representações granulares de um conjunto de permissões. Um ROLE_READER teria apenas autoridade de leitura ou exibição, enquanto um ROLE_EDITOR teria leitura e gravação. As funções são usadas principalmente para uma primeira triagem nos arredores do processamento de solicitações, como http. ... .antMatcher (...). hasRole (ROLE_MANAGER)

As autoridades sendo aplicadas profundamente no fluxo do processo da solicitação permitem uma aplicação mais refinada da permissão. Por exemplo, um usuário pode ter permissão de leitura e gravação para primeiro nível de um recurso, mas apenas leitura para um sub-recurso. Ter um ROLE_READER restringiria seu direito de editar o recurso de primeiro nível, pois ele precisa da permissão de gravação para editar esse recurso, mas um interceptor @PreAuthorize poderia bloquear seu tentativa de editar o sub-recurso.

Jake

softjake
fonte
2

Como outros já mencionaram, penso nas funções como contêineres para permissões mais granulares.

Embora eu ache que a implementação da Função de Hierarquia está sem controle fino dessas permissões granulares.
Então, criei uma biblioteca para gerenciar os relacionamentos e injetar as permissões como autoridades concedidas no contexto de segurança.

Talvez eu tenha um conjunto de permissões no aplicativo, como CREATE, READ, UPDATE, DELETE, que são associadas à função do usuário.

Ou permissões mais específicas, como READ_POST, READ_PUBLISHED_POST, CREATE_POST, PUBLISH_POST

Essas permissões são relativamente estáticas, mas o relacionamento das funções com elas pode ser dinâmico.

Exemplo -

@Autowired 
RolePermissionsRepository repository;

public void setup(){
  String roleName = "ROLE_ADMIN";
  List<String> permissions = new ArrayList<String>();
  permissions.add("CREATE");
  permissions.add("READ");
  permissions.add("UPDATE");
  permissions.add("DELETE");
  repository.save(new RolePermissions(roleName, permissions));
}

Você pode criar APIs para gerenciar o relacionamento dessas permissões com uma função.

Não quero copiar / colar outra resposta, então aqui está o link para uma explicação mais completa sobre SO.
https://stackoverflow.com/a/60251931/1308685

Para reutilizar minha implementação, criei um repositório. Por favor sinta-se livre para contribuir!
https://github.com/savantly-net/spring-role-permissions

Jeremy
fonte