Diferencia entre rol y autoridad otorgada en Spring Security

227

Hay conceptos e implementaciones en Spring Security, como la GrantedAuthorityinterfaz para obtener una autoridad para autorizar / controlar un acceso.

Me gustaría eso para las operaciones permitidas, como createSubUsers o deleteAccounts , que permitiría a un administrador (con función ROLE_ADMIN).

Me estoy confundiendo con los tutoriales / demostraciones que veo en línea. Intento conectar lo que leo, pero creo que tratamos a los dos de manera intercambiable.

¿Veo hasRoleconsumir una GrantedAuthoritycuerda? Definitivamente lo estoy haciendo mal en la comprensión. ¿Cuáles son estos conceptualmente en Spring Security?

¿Cómo almaceno el rol de un usuario, separado de las autoridades para ese rol?

También estoy mirando la org.springframework.security.core.userdetails.UserDetailsinterfaz que se utiliza en el DAO referenciado por el proveedor de autenticación, que consume una User(tenga en cuenta la última autoridad otorgada):

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

¿O hay alguna otra forma de diferenciar los otros dos? ¿O no es compatible y tenemos que hacer el nuestro?

Chinmay
fuente

Respuestas:

365

Piense en una Autoridad concedida como un "permiso" o un "derecho". Esos "permisos" se expresan (normalmente) como cadenas (con el getAuthority()método). Esas cadenas le permiten identificar los permisos y dejar que sus votantes decidan si otorgan acceso a algo.

Puede otorgar diferentes GrantedAuthoritys (permisos) a los usuarios poniéndolos en el contexto de seguridad. Normalmente lo hace implementando su propio UserDetailsService que devuelve una implementación de UserDetails que devuelve las GrantedAuthorities necesarias.

Los roles (como se usan en muchos ejemplos) son solo "permisos" con una convención de nomenclatura que dice que un rol es una GrantedAuthority que comienza con el prefijo ROLE_. No hay nada mas. Un rol es solo una Autoridad otorgada - un "permiso" - un "derecho". Verá muchos lugares en la seguridad de primavera donde el rol con su ROLE_prefijo se maneja especialmente, por ejemplo, en RoleVoter, donde el ROLE_prefijo se usa por defecto. Esto le permite proporcionar los nombres de los roles sin el ROLE_prefijo. Antes de Spring Security 4, este manejo especial de "roles" no se seguía de manera muy consistente y las autoridades y los roles a menudo se trataban de la misma manera (por ejemplo,hasAuthority()hasRole()) Con Spring Security 4, el tratamiento de roles es más consistente y el código que trata con "roles" (como el RoleVoter, la hasRoleexpresión, etc.) siempre agrega el ROLE_prefijo para usted. Entonces hasAuthority('ROLE_ADMIN')significa lo mismo que hasRole('ADMIN')porque el ROLE_prefijo se agrega automáticamente. Consulte la guía de migración de Spring Security 3 a 4 para obtener más información.

Pero aún así: un rol es solo una autoridad con un ROLE_prefijo especial . Entonces, en Spring security 3 @PreAuthorize("hasRole('ROLE_XYZ')")es igual @PreAuthorize("hasAuthority('ROLE_XYZ')")y en Spring security 4 @PreAuthorize("hasRole('XYZ')")es igual que @PreAuthorize("hasAuthority('ROLE_XYZ')").

Con respecto a su caso de uso:

Los usuarios tienen roles y los roles pueden realizar ciertas operaciones.

Podría terminar en GrantedAuthoritieslos roles a los que pertenece un usuario y las operaciones que puede desempeñar un rol. Los GrantedAuthoritiesroles tienen el prefijo ROLE_y las operaciones tienen el prefijo OP_. Un ejemplo para las autoridades de operación podrían ser OP_DELETE_ACCOUNT, OP_CREATE_USER, OP_RUN_BATCH_JOBetc. roles pueden ser ROLE_ADMIN, ROLE_USER, ROLE_OWNERetc.

Podría terminar haciendo que sus entidades se implementen GrantedAuthoritycomo en este ejemplo (pseudocó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;
    }
}

Los identificadores de los roles y operaciones que crea en su base de datos serían la representación de GrantedAuthority, por ejemplo ROLE_ADMIN, OP_DELETE_ACCOUNTetc. Cuando un usuario se autentica, asegúrese de que todas las GrantedAuthorities de todos sus roles y las operaciones correspondientes se devuelvan desde UserDetails.getAuthorities () método.

Ejemplo: El rol de administrador con id ROLE_ADMINtiene operaciones OP_DELETE_ACCOUNT, OP_READ_ACCOUNT, OP_RUN_BATCH_JOBasignados a la misma. El rol de usuario con id ROLE_USERtiene la operación OP_READ_ACCOUNT.

Si un administrador registros en el contexto de seguridad resultantes tendrán las GrantedAuthorities: ROLE_ADMIN, OP_DELETE_ACCOUNT, OP_READ_ACCOUNT,OP_RUN_BATCH_JOB

Si un usuario inicia una sesión que, tendrá: ROLE_USER,OP_READ_ACCOUNT

UserDetailsService se encargaría de recopilar todos los roles y todas las operaciones de esos roles y ponerlos a disposición mediante el método getAuthorities () en la instancia de UserDetails devuelta.

James
fuente
2
¡Gracias! He estado buscando en todas partes por qué "hasRole ('rolename')" no estaba funcionando en Spring 4 -> Su documentación no fue exactamente rápida de leer. ¡Solo un rápido "buscar y reemplazar" y estoy de nuevo en camino!
Jørgen Skår Fischer
12
Esta es una respuesta genial. Una cosa es dejar claro a explicar un poco diferente, el hasRole('xyz')de la primavera de Seguridad 4 espera que se tenga la ROLE_ prefijo mientras que el hasAuthority('xyz')no espera que el prefijo y evalúa exactamente lo que se pasa en. He utilizado esta solución, pero se ejecuta en problemas con el hasRole('OP_MY_PERMISSION')puesto la ROLE_ se necesitaba prefijo. En cambio, debería haber estado usando el hasAuthority('OP_MY_PERMISSION')ya que no tenía el prefijo.
randal4
@james puedes mirar esta pregunta stackoverflow.com/questions/41500031/…
saurabh kumar
1
En springframework.org/security/tags JSTL <sec:authorize access="hasRole('ADMIN')">es lo mismo que<sec:authorize access="hasRole('ROLE_ADMIN')">
Alex78191
1
Si. OP_ es solo un prefijo arbitrario. ROLE_ tiene su significado especial en algunas implementaciones de primavera.
James
9

AFAIK Granted La autoridad y los roles son los mismos en seguridad de primavera. La cadena getAuthority () de GrantedAuthority es el rol (según la implementación predeterminada SimpleGrantedAuthority).

Para su caso, puede utilizar roles jerárquicos

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

No es el sol exacto que buscas, pero espero que ayude

Editar : responde a tu comentario

El papel es como un permiso en la seguridad de primavera. El uso de intercept-url con hasRole proporciona un control muy detallado de qué operación está permitida para qué rol / permiso.

La forma en que manejamos nuestra aplicación es que definimos el permiso (es decir, el rol) para cada operación (o URL de reposo) para, por ejemplo, view_account, delete_account, add_account, etc. Luego creamos perfiles lógicos para cada usuario como admin, guest_user, normal_user. Los perfiles son solo agrupaciones lógicas de permisos, independientes de spring-security. Cuando se agrega un nuevo usuario, se le asigna un perfil (que tiene todos los permisos permitidos). Ahora, cuando el usuario intenta realizar alguna acción, el permiso / rol para esa acción se verifica contra las autoridades otorgadas por el usuario.

Además, el RoleVoter predeterminado usa el prefijo ROLE_, por lo que cualquier autoridad que comience con ROLE_ se considera un rol, puede cambiar este comportamiento predeterminado usando un RolePrefix personalizado en el votante de roles y usándolo en la seguridad de primavera.

descifrador
fuente
1
Gracias por tu ayuda. La jerarquía parece ser otra cosa. La forma en que pienso hacerlo ahora (si tengo que usar Spring Security) es almacenar tanto el rol como la autoridad en la misma lista y usar el predicado hasRole para realizar las comprobaciones de ambos. Pensando más en esto, ¿probablemente esto sea dejado intencional por los muchachos de Spring Security? ¿Existe la posibilidad de utilizar la intercepción-url que no sea verificar usando hasRole en la lista completa de Roles Y Privilegios / Autoridades u otras aplicaciones de autorización? Además, ¿cuál es la necesidad de prefijar ROLE_? ¿Es convencional?
Chinmay
7

Otra forma de entender la relación entre estos conceptos es interpretar un PAPEL como un contenedor de Autoridades.

Las autoridades son permisos específicos que se dirigen a una acción específica, a veces junto con un alcance o contexto de datos específicos. Por ejemplo, Leer, Escribir, Administrar, puede representar varios niveles de permisos para un alcance de información dado.

Además, las autoridades se imponen profundamente en el flujo de procesamiento de una solicitud, mientras que el PAPEL se filtra por el filtro de solicitud antes de llegar al Controlador. Las mejores prácticas prescriben la implementación de la aplicación de las autoridades más allá del controlador en la capa empresarial.

Por otro lado, los ROLES son una representación de grano grueso de un conjunto de permisos. Un ROLE_READER solo tendría autoridad de lectura o vista, mientras que un ROLE_EDITOR tendría tanto lectura como escritura. Los roles se utilizan principalmente para una primera evaluación en la periferia del procesamiento de solicitudes, como http. ... .antMatcher (...). hasRole (ROLE_MANAGER)

Las Autoridades que se aplican en profundidad en el flujo del proceso de la solicitud permiten una aplicación más precisa del permiso. Por ejemplo, un usuario puede tener permiso de lectura y escritura para primer nivel de un recurso, pero solo lectura para un subrecurso. Tener un ROLE_READER restringiría su derecho a editar el recurso de primer nivel, ya que necesita el permiso de escritura para editar este recurso, pero un interceptor @PreAuthorize podría bloquear su tentativa de editar el sub-recurso.

Jake

softjake
fuente
2

Como otros han mencionado, pienso en los roles como contenedores para permisos más granulares.

Aunque descubrí que la implementación del rol de jerarquía carece de un control preciso de estos permisos granulares.
Así que creé una biblioteca para administrar las relaciones e inyectar los permisos como autoridades otorgadas en el contexto de seguridad.

Es posible que tenga un conjunto de permisos en la aplicación, algo como CREAR, LEER, ACTUALIZAR, ELIMINAR, que luego se asocian con el Rol del usuario.

O permisos más específicos como READ_POST, READ_PUBLISHED_POST, CREATE_POST, PUBLISH_POST

Estos permisos son relativamente estáticos, pero la relación de roles con ellos puede ser dinámica.

Ejemplo

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

Puede crear API para administrar la relación de estos permisos con un rol.

No quiero copiar / pegar otra respuesta, así que aquí está el enlace a una explicación más completa sobre SO.
https://stackoverflow.com/a/60251931/1308685

Para reutilizar mi implementación, creé un repositorio. ¡Por favor siéntase libre de contribuir!
https://github.com/savantly-net/spring-role-permissions

Jeremy
fuente