Comment vérifier «hasRole» dans Java Code avec Spring Security?


118

Comment vérifier les droits ou autorisations des utilisateurs dans Java Code? Par exemple - Je veux afficher ou masquer le bouton pour l'utilisateur en fonction du rôle. Il y a des annotations comme:

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

Comment le faire en code Java? Quelque chose comme :

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

Réponses:


70

Spring Security 3.0 a cette API

SecurityContextHolderAwareRequestWrapper.isUserInRole(String role)

Vous devrez injecter le wrapper avant de l'utiliser.

SecurityContextHolderAwareRequestWrapper


53
Comme votre réponse est énoncée, il semble que la méthode soit statique, mais vous avez besoin d'une SecurityContextHolderAwareRequestWrapperinstance. Vous pouvez l'améliorer en expliquant comment l'obtenir et en clarifiant un peu plus la réponse elle-même.
Xtreme Biker

3
Comment puis-je récupérer le wrapper dans le contrôleur?
Alfonso Tienda

3
Comment puis-je obtenir une instance de SecurityContextHolderAwareRequestWrapper?
gstackoverflow

2
Xtreme Biker a raison, comment obtenez-vous la classe SecurityContextHolderAwareRequestWrapper? Ce n'est pas un objet statique.
Essayez-le le

5
S'il s'agissait d'une application Web, ce qui semble ne pas être le cas, vous pouvez simplement ajouter SecurityContextHolderAwareRequestWrapper en tant que paramètre. Et s'il s'agissait d'une application Web, vous pouvez simplement déclarer HttpServletRequest en tant que paramètre et appeler isUserInRole
David Bradley

144

vous pouvez utiliser la méthode isUserInRole de l'objet HttpServletRequest.

quelque chose comme:

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


    if (request.isUserInRole("ROLE_ADMIN")) {
        // code here
    }
}

plus facile à tester je pense
fego

1
mais si je n'ai pas demandé?
gstackoverflow

Et ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest()pour obtenir la demande? :)
Petr Újezdský

4
@Autowired HttpServletRequest request; ?
Pascal

Et ce n'est même pas une API Spring, une spécification de servlet simple! Une honte qui n'est pas la réponse choisie
gregfqt

67

Au lieu d'utiliser une boucle pour trouver l'autorité à partir de UserDetails, vous pouvez faire:

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
boolean authorized = authorities.contains(new SimpleGrantedAuthority("ROLE_ADMIN"));

2
Une réponse beaucoup plus agréable, cependant le ROLE_ADMIN doit être entre guillemets.
Erica Kane

6
C'est très risqué. Sachez que le passage à une autre implémentation de l'implémentation GrantedAuthority (par exemple JAAS, en ajoutant une autre possibilité d'autorisation) entraînera un dysfonctionnement de ce code. Voir l'implémentation equals () dans SimpleGrantedAuthority
Petr Újezdský

47

Vous pouvez récupérer le contexte de sécurité, puis l'utiliser:

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

SecurityContextHolder.getContext()n'est jamais NULL, consultez la documentation. Ainsi, vous pouvez éviter de vérifier le contexte NULL.
Imtiaz Shakil Siddique

14

Vous pouvez implémenter une méthode hasRole () comme ci-dessous - (Ceci est testé sur Spring Security 3.0.x pas sûr des autres versions.)

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

1
SecurityContextHolder.getContext().getAuthentication()peut récupérer null. Peut-être que vous ajoutez un chèque?
Mrusful

10

J'utilise ceci:

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

8

Vous pouvez obtenir de l'aide de la classe AuthorityUtils . Vérification du rôle en tant que one-liner:

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

Attention: cela ne vérifie pas la hiérarchie des rôles, si elle existe.


C'est la solution la plus simple car je dois vérifier la liste plusieurs fois et la récupérer une fois avec une routine simple et magique est géniale!
LeO

6

La réponse de JoseK ne peut pas être utilisée lorsque vous êtes dans votre couche de service, où vous ne voulez pas introduire de couplage avec la couche Web de la référence à la requête HTTP. Si vous envisagez de résoudre les rôles dans la couche de service, la réponse de Gopi est la voie à suivre.

Cependant, c'est un peu long. Les autorités sont accessibles directement à partir de l'authentification. Par conséquent, si vous pouvez supposer que vous avez un utilisateur connecté, ce qui suit le fait:

/**
 * @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;
}

6

La plupart des réponses manquent certains points:

  1. Le rôle et l'autorité ne sont pas la même chose au printemps. Voir ici pour plus de détails.

  2. Les noms de rôle sont égaux à rolePrefix+ authority.

  3. Le préfixe de rôle par défaut est ROLE_cependant configurable. Regardez ici .

Par conséquent, une vérification de rôle appropriée doit respecter le préfixe de rôle s'il est configuré.

Malheureusement, la personnalisation du préfixe de rôle dans Spring est un peu piratée, dans de nombreux endroits, le préfixe par défaut ROLE_est codé en dur, mais en plus de cela, un bean de type GrantedAuthorityDefaultsest vérifié dans le contexte Spring, et s'il existe, le préfixe de rôle personnalisé il a est respecté.

En rassemblant toutes ces informations, une meilleure implémentation du vérificateur de rôle serait quelque chose comme:

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

3

Curieusement, je ne pense pas qu'il existe une solution standard à ce problème, car le contrôle d'accès de sécurité par ressort est basé sur l'expression et non sur Java. vous pouvez vérifier le code source de DefaultMethodSecurityExpressionHandler pour voir si vous pouvez réutiliser quelque chose qu'ils font là-bas


Donc, votre solution est d'utiliser DefaultMethodSecurityExpressionHandler comme bean et d'obtenir un analyseur d'expression et de le vérifier dans EL?
Piotr Gwiazda

cela ne fonctionnera probablement pas, car le gestionnaire opère sur des invocations de méthode (que vous n'avez pas dans votre contexte). vous devez probablement créer votre propre bean qui fait quelque chose de similaire, mais sans utiliser de contexte d'invocation de méthode
Sean Patrick Floyd

2

Mieux vaut tard que jamais, laissez-moi mettre ma valeur de 2 cents.

Dans le monde JSF, dans mon bean géré, j'ai fait ce qui suit:


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

Comme mentionné ci-dessus, je crois comprendre que cela peut être fait de la manière suivante:


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

2

Cela vient en quelque sorte de la question de l'autre côté, mais j'ai pensé que je la jetterais car je devais vraiment fouiller sur Internet pour le savoir.

Il y a beaucoup de choses sur la façon de vérifier les rôles, mais pas beaucoup de dire ce que vous vérifiez réellement quand vous dites hasRole ("bla")

HasRole vérifie les autorités accordées pour le principal actuellement authentifié

Donc vraiment quand vous voyez hasRole ("blah") signifie vraiment hasAuthority ("blah") .

Dans le cas que j'ai vu, vous faites cela avec une classe qui implémente UserDetails qui définit une méthode appelée getAuthorities. En cela, vous en ajouterez essentiellement new SimpleGrantedAuthority("some name")à une liste basée sur une logique. Les noms de cette liste sont les éléments vérifiés par les instructions hasRole.

Je suppose que dans ce contexte, l'objet UserDetails est le principal actuellement authentifié. Il y a de la magie qui se produit dans et autour des fournisseurs d'authentification et plus particulièrement dans le gestionnaire d'authentification qui rend cela possible.


2
Depuis Spring Security 4.0, cela hasRole("bla")équivaut désormais à hasAuthority("ROLE_bla").
lanoxx le

2

La réponse @gouki est la meilleure!

Juste un conseil sur la façon dont le printemps fait vraiment cela.

Il existe une classe nommée SecurityContextHolderAwareRequestWrapperqui implémente la ServletRequestWrapperclasse.

Le SecurityContextHolderAwareRequestWrapperremplace isUserInRoleet recherche l'utilisateur Authentication(qui est géré par Spring) pour savoir si l'utilisateur a un rôle ou non.

SecurityContextHolderAwareRequestWrapper le code est comme:

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

2

Ces deux annotations ci-dessous sont égales, "hasRole" ajoutera automatiquement le préfixe "ROLE_". Assurez-vous d'avoir la bonne annotation. Ce rôle est défini dans UserDetailsService # loadUserByUsername.

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

ensuite, vous pouvez obtenir le rôle dans le code java.

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

1

Dans notre projet, nous utilisons une hiérarchie des rôles, alors que la plupart des réponses ci-dessus visent uniquement à vérifier un rôle spécifique, c'est-à-dire ne vérifieraient que le rôle donné, mais pas ce rôle et remonter la hiérarchie.

Une solution pour cela:

@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 est défini comme un bean dans spring-security.xml.


1
Ou vous pouvez remplir vos rôles correctement: github.com/spring-projects/spring-security/issues/…
arctica

1

Sur votre modèle utilisateur, ajoutez simplement une méthode 'hasRole' comme ci-dessous

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

Je l'utilise généralement pour vérifier si l'utilisateur authentifié a le rôle d'administrateur comme suit

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

1

Les rôles des utilisateurs peuvent être vérifiés des manières suivantes:

  1. Utilisation des méthodes statiques d'appel dans SecurityContextHolder:

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

  2. Utilisation de HttpServletRequest

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


0

Mon approche avec l'aide de Java8, passer des rôles séparés par une virgule vous donnera vrai ou faux

    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;
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.