¿Cómo verificar "hasRole" en el código Java con Spring Security?

118

¿Cómo verificar la autoridad o el permiso del usuario en el código Java? Por ejemplo, quiero mostrar u ocultar el botón para el usuario según el rol. Hay anotaciones como:

@PreAuthorize("hasRole('ROLE_USER')")

¿Cómo hacerlo en código Java? Algo como :

if(somethingHere.hasRole("ROLE_MANAGER")) {
   layout.addComponent(new Button("Edit users"));
}
Piotr Gwiazda
fuente

Respuestas:

70

Spring Security 3.0 tiene esta API

SecurityContextHolderAwareRequestWrapper.isUserInRole(String role)

Tendrás que inyectar el envoltorio antes de usarlo.

SecurityContextHolderAwareRequestWrapper

JoseK
fuente
53
Como se indica su respuesta, parece que el método es estático, sin embargo, necesita una SecurityContextHolderAwareRequestWrapperinstancia. Podrías mejorarlo explicando cómo obtenerlo y aclarando un poco más la respuesta en sí.
Xtreme Biker
3
¿Cómo puedo recuperar el contenedor en el controlador?
Alfonso Tienda
3
¿Cómo puedo obtener una instancia de SecurityContextHolderAwareRequestWrapper?
gstackoverflow
2
Xtreme Biker tiene razón, ¿cómo se obtiene la clase SecurityContextHolderAwareRequestWrapper? No es un objeto estático.
Pruébelo el
5
Si se trataba de una aplicación web, que parece que no lo es, puede agregar SecurityContextHolderAwareRequestWrapper como parámetro. Y si fuera una aplicación web, simplemente podría declarar HttpServletRequest como parámetro y llamar a isUserInRole
David Bradley
144

puede utilizar el método isUserInRole del objeto HttpServletRequest.

algo como:

public String createForm(HttpSession session, HttpServletRequest request,  ModelMap   modelMap) {


    if (request.isUserInRole("ROLE_ADMIN")) {
        // code here
    }
}
gouki
fuente
más fácil de probar, creo
fego
1
pero si no lo he pedido?
gstackoverflow
¿Qué pasa ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest()con la solicitud? :)
Petr Újezdský
4
@Autowired HttpServletRequest request; ?
Pascal
¡Y eso ni siquiera es una API de Spring, una especificación simple de Servlet! Una pena que no es la respuesta elegida
gregfqt
67

En lugar de usar un bucle para encontrar la autoridad de UserDetails, puede hacer:

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
boolean authorized = authorities.contains(new SimpleGrantedAuthority("ROLE_ADMIN"));
Ninca
fuente
2
Una respuesta mucho mejor, sin embargo, ROLE_ADMIN debería estar entre comillas dobles.
Erica Kane
6
Esto es muy arriesgado. Tenga en cuenta que cambiar a otra implementación de la implementación de GrantedAuthority (por ejemplo, JAAS, al agregar otra posibilidad de autorización) hará que este código no funcione correctamente. Ver implementación equals () en SimpleGrantedAuthority
Petr Újezdský
47

Puede recuperar el contexto de seguridad y luego usarlo:

    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.context.SecurityContext;
    import org.springframework.security.core.context.SecurityContextHolder;

    protected boolean hasRole(String role) {
        // get security context from thread local
        SecurityContext context = SecurityContextHolder.getContext();
        if (context == null)
            return false;

        Authentication authentication = context.getAuthentication();
        if (authentication == null)
            return false;

        for (GrantedAuthority auth : authentication.getAuthorities()) {
            if (role.equals(auth.getAuthority()))
                return true;
        }

        return false;
    }
Nate Sammons
fuente
SecurityContextHolder.getContext()es nunca NULL, consulte los documentos. Por lo tanto, puede evitar verificar el contexto NULL.
Imtiaz Shakil Siddique
14

Puede implementar un método hasRole () como se muestra a continuación: (esto se probó en Spring Security 3.0.x, no estoy seguro de otras versiones).

  protected final boolean hasRole(String role) {
    boolean hasRole = false;
    UserDetails userDetails = getUserDetails();
    if (userDetails != null) {
      Collection<GrantedAuthority> authorities = userDetails.getAuthorities();
      if (isRolePresent(authorities, role)) {
        hasRole = true;
      }
    } 
    return hasRole;
  }
  /**
   * Get info about currently logged in user
   * @return UserDetails if found in the context, null otherwise
   */
  protected UserDetails getUserDetails() {
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    UserDetails userDetails = null;
    if (principal instanceof UserDetails) {
      userDetails = (UserDetails) principal;
    }
    return userDetails;
  }
  /**
   * Check if a role is present in the authorities of current user
   * @param authorities all authorities assigned to current user
   * @param role required authority
   * @return true if role is present in list of authorities assigned to current user, false otherwise
   */
  private boolean isRolePresent(Collection<GrantedAuthority> authorities, String role) {
    boolean isRolePresent = false;
    for (GrantedAuthority grantedAuthority : authorities) {
      isRolePresent = grantedAuthority.getAuthority().equals(role);
      if (isRolePresent) break;
    }
    return isRolePresent;
  }
Gopi
fuente
1
SecurityContextHolder.getContext().getAuthentication()puede recuperar null. ¿Quizás agregue algún cheque?
Mrusful
10

Estoy usando esto:

@RequestMapping(method = RequestMethod.GET)
public void welcome(SecurityContextHolderAwareRequestWrapper request) {
    boolean b = request.isUserInRole("ROLE_ADMIN");
    System.out.println("ROLE_ADMIN=" + b);

    boolean c = request.isUserInRole("ROLE_USER");
    System.out.println("ROLE_USER=" + c);
}
Joey Jarin
fuente
8

Puede obtener ayuda de la clase AuthorityUtils . Verificación de la función como una sola línea:

if (AuthorityUtils.authorityListToSet(SecurityContextHolder.getContext().getAuthentication().getAuthorities()).contains("ROLE_MANAGER")) {
    /* ... */
}

Advertencia: esto no comprueba la jerarquía de funciones, si existe.

holmis83
fuente
Esa es la solución más simple porque necesito revisar la Lista varias veces y buscarla una vez con una rutina mágica simple es genial.
LeO
6

La respuesta de JoseK no se puede usar cuando está en su capa de servicio, donde no desea introducir un acoplamiento con la capa web desde la referencia a la solicitud HTTP. Si está buscando resolver los roles mientras está en la capa de servicio, la respuesta de Gopi es el camino a seguir.

Sin embargo, es un poco largo. Se puede acceder a las autoridades directamente desde la autenticación. Por lo tanto, si puede asumir que tiene un usuario conectado, lo siguiente lo hace:

/**
 * @return true if the user has one of the specified roles.
 */
protected boolean hasRole(String[] roles) {
    boolean result = false;
    for (GrantedAuthority authority : SecurityContextHolder.getContext().getAuthentication().getAuthorities()) {
        String userRole = authority.getAuthority();
        for (String role : roles) {
            if (role.equals(userRole)) {
                result = true;
                break;
            }
        }

        if (result) {
            break;
        }
    }

    return result;
}
Hombre estrella
fuente
6

A la mayoría de las respuestas les faltan algunos puntos:

  1. El rol y la autoridad no son lo mismo en Spring. Consulte aquí para obtener más detalles.

  2. Los nombres de los roles son iguales a rolePrefix+ authority.

  3. ROLE_Sin embargo, el prefijo de rol predeterminado es configurable. Vea aquí .

Por lo tanto, una verificación de función adecuada debe respetar el prefijo de función si está configurado.

Desafortunadamente, la personalización del prefijo de rol en Spring es un poco hacky, en muchos lugares el prefijo predeterminado ROLE_está codificado, pero además de eso, un bean de tipoGrantedAuthorityDefaults se verifica en el contexto de Spring, y si existe, el prefijo de rol personalizado se respeta.

Al reunir toda esta información, una mejor implementación del verificador de roles sería algo como:

@Component
public class RoleChecker {

    @Autowired(required = false)
    private GrantedAuthorityDefaults grantedAuthorityDefaults;

    public boolean hasRole(String role) {
        String rolePrefix = grantedAuthorityDefaults != null ? grantedAuthorityDefaults.getRolePrefix() : "ROLE_";
        return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
                .map(Authentication::getAuthorities)
                .map(Collection::stream)
                .orElse(Stream.empty())
                .map(GrantedAuthority::getAuthority)
                .map(authority -> rolePrefix + authority)
                .anyMatch(role::equals);
    }
}
Utku Özdemir
fuente
3

Por extraño que parezca, no creo que exista una solución estándar para este problema, ya que el control de acceso de seguridad de primavera se basa en expresiones , no en Java. puede consultar el código fuente de DefaultMethodSecurityExpressionHandler para ver si puede reutilizar algo que están haciendo allí

Sean Patrick Floyd
fuente
Entonces, ¿su solución es usar DefaultMethodSecurityExpressionHandler como bean y obtener un analizador de expresiones y verificarlo en EL?
Piotr Gwiazda
eso probablemente no funcionará, ya que el controlador opera en invocaciones de métodos (que no tiene en su contexto). probablemente necesite crear su propio bean que haga algo similar, pero sin usar un contexto de invocación de método
Sean Patrick Floyd
2

Mejor tarde que nunca, déjame poner mis 2 centavos.

En el mundo JSF, dentro de mi bean administrado, hice lo siguiente:


HttpServletRequest req = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
SecurityContextHolderAwareRequestWrapper sc = new SecurityContextHolderAwareRequestWrapper(req, "");

Como se mencionó anteriormente, tengo entendido que se puede hacer de la siguiente manera:


Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserDetails userDetails = null;
if (principal instanceof UserDetails) {
    userDetails = (UserDetails) principal;
    Collection  authorities = userDetails.getAuthorities();
}
Lukec
fuente
2

Esto es como llegar a la pregunta desde el otro lado, pero pensé en incluirla ya que realmente tuve que investigar en Internet para averiguarlo.

Hay muchas cosas sobre cómo verificar roles, pero no se dice mucho sobre lo que realmente está verificando cuando dice hasRole ("bla")

HasRole comprueba las autoridades otorgadas para el principal actualmente autenticado

Así que realmente cuando ves hasRole ("bla") realmente significa hasAuthority ("bla") .

En el caso que he visto, haces esto con una clase que implementa UserDetails que define un método llamado getAuthorities. En esto básicamente agregarás algunosnew SimpleGrantedAuthority("some name") a una lista basada en cierta lógica. Los nombres en esta lista son las cosas verificadas por las declaraciones hasRole.

Supongo que en este contexto, el objeto UserDetails es el principal actualmente autenticado. Hay algo de magia que ocurre dentro y alrededor de los proveedores de autenticación y, más específicamente, el administrador de autenticación que hace que esto suceda.

JonnyRaa
fuente
2
A partir de Spring Security 4.0, hasRole("bla")ahora es igual a hasAuthority("ROLE_bla").
lanoxx
2

¡La respuesta de @gouki es la mejor!

Solo un consejo de cómo la primavera realmente hace esto.

Hay una clase llamada SecurityContextHolderAwareRequestWrapperque implementa elServletRequestWrapper clase.

El SecurityContextHolderAwareRequestWrapperanula el isUserInRoleusuario de búsqueda yAuthentication (que es administrado por Spring) para encontrar si el usuario tiene un rol o no.

SecurityContextHolderAwareRequestWrapper el código es como:

    @Override
    public boolean isUserInRole(String role) {
        return isGranted(role);
    }

 private boolean isGranted(String role) {
        Authentication auth = getAuthentication();

        if( rolePrefix != null ) {
            role = rolePrefix + role;
        }

        if ((auth == null) || (auth.getPrincipal() == null)) {
            return false;
        }

        Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();

        if (authorities == null) {
            return false;
        }

        //This is the loop which do actual search
        for (GrantedAuthority grantedAuthority : authorities) {
            if (role.equals(grantedAuthority.getAuthority())) {
                return true;
            }
        }

        return false;
    }
Alireza Fattahi
fuente
2

Estas dos anotaciones a continuación son iguales, "hasRole" agregará automáticamente el prefijo "ROLE_". Asegúrate de tener la anotación correcta. Este rol se establece en UserDetailsService # loadUserByUsername.

@PreAuthorize("hasAuthority('ROLE_user')")
@PreAuthorize("hasRole('user')")

luego, puede obtener el rol en el código java.

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_user"))){
    System.out.println("user role2");
}
Luke Cheung
fuente
1

En nuestro proyecto, estamos usando una jerarquía de roles, mientras que la mayoría de las respuestas anteriores solo apuntan a verificar un rol específico, es decir, solo verificarían el rol asignado, pero no ese rol y ascenderían en la jerarquía.

Una solución para esto:

@Component
public class SpringRoleEvaluator {

@Resource(name="roleHierarchy")
private RoleHierarchy roleHierarchy;

public boolean hasRole(String role) {
    UserDetails dt = AuthenticationUtils.getSessionUserDetails();

    for (GrantedAuthority auth: roleHierarchy.getReachableGrantedAuthorities(dt.getAuthorities())) {
        if (auth.toString().equals("ROLE_"+role)) {
            return true;
        }
    }
    return false;
}

RoleHierarchy se define como un bean en spring-security.xml.

Kirinya
fuente
1
O puede completar sus roles correctamente: github.com/spring-projects/spring-security/issues/…
arctica
1

En su modelo de usuario, simplemente agregue un método 'hasRole' como el siguiente

public boolean hasRole(String auth) {
    for (Role role : roles) {
        if (role.getName().equals(auth)) { return true; }
    }
    return false;
}

Normalmente lo uso para verificar si el usuario autenticado tiene el rol de administrador de la siguiente manera

Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // This gets the authentication
User authUser = (User) authentication.getPrincipal(); // This gets the logged in user
authUser.hasRole("ROLE_ADMIN") // This returns true or false
user8174892
fuente
1

Los roles de usuario se pueden verificar de las siguientes maneras:

  1. Usando métodos estáticos de llamada en SecurityContextHolder:

    Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && auth.getAuthorities().stream().anyMatch(role -> role.getAuthority().equals("ROLE_NAME"))) { //do something}

  2. Usando HttpServletRequest

@GetMapping("/users")
public String getUsers(HttpServletRequest request) {
    if (request.isUserInRole("ROLE_NAME")) {
      
    }

Suerte
fuente
0

My Approach con la ayuda de Java8, pasar roles separados en coma le dará verdadero o falso

    public static Boolean hasAnyPermission(String permissions){
    Boolean result = false;
    if(permissions != null && !permissions.isEmpty()){
        String[] rolesArray = permissions.split(",");
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        for (String role : rolesArray) {
            boolean hasUserRole = authentication.getAuthorities().stream().anyMatch(r -> r.getAuthority().equals(role));
            if (hasUserRole) {
                result = true;
                break;
            }
        }
    }
    return result;
}
Jajikanth pydimarla
fuente