Come controllare "hasRole" nel codice Java con Spring Security?


118

Come controllare l'autorizzazione o l'autorizzazione dell'utente nel codice Java? Ad esempio, voglio mostrare o nascondere il pulsante per l'utente a seconda del ruolo. Ci sono annotazioni come:

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

Come farlo in codice Java? Qualcosa di simile a :

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

Risposte:


70

Spring Security 3.0 ha questa API

SecurityContextHolderAwareRequestWrapper.isUserInRole(String role)

Dovrai iniettare l'involucro prima di usarlo.

SecurityContextHolderAwareRequestWrapper


53
Poiché la tua risposta è dichiarata, sembra che il metodo sia statico, tuttavia hai bisogno di SecurityContextHolderAwareRequestWrapperun'istanza. Potresti migliorarlo spiegando come ottenerlo e chiarendo un po 'di più la risposta stessa.
Xtreme Biker

3
Come posso recuperare il wrapper nel controller?
Alfonso Tienda

3
Come posso ottenere l'istanza di SecurityContextHolderAwareRequestWrapper?
gstackoverflow

2
Xtreme Biker ha ragione, come si ottiene la classe SecurityContextHolderAwareRequestWrapper? Non è un oggetto statico.
Provalo il

5
Se si tratta di un'app Web, che sembra non lo sia, è sufficiente aggiungere SecurityContextHolderAwareRequestWrapper come parametro. E se fosse un'app web potresti semplicemente dichiarare HttpServletRequest come parametro e chiamare isUserInRole
David Bradley

144

è possibile utilizzare il metodo isUserInRole dell'oggetto HttpServletRequest.

qualcosa di simile a:

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


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

più facile da testare penso
fego

1
ma se non ho richiesta?
gstackoverflow

Che ne dici ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest()di ottenere la richiesta? :)
Petr Újezdský

4
@Autowired HttpServletRequest richiesta; ?
Pascal

E non è nemmeno un'API Spring, una semplice specifica Servlet! Peccato che non sia la risposta scelta
gregfqt

67

Invece di usare un ciclo per trovare l'autorità da UserDetails puoi fare:

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

2
Una risposta molto più carina, tuttavia ROLE_ADMIN dovrebbe essere tra virgolette doppie.
Erica Kane

6
Questo è molto rischioso. Tieni presente che il passaggio a un'altra implementazione dell'implementazione di GrantedAuthority (ad es. JAAS, aggiungendo un'altra possibilità di autorizzazione) renderà questo codice malfunzionante. Vedere l'implementazione di equals () in
SimpleGrantedAuthority

47

Puoi recuperare il contesto di sicurezza e quindi 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;
    }

SecurityContextHolder.getContext()non è mai NULL, controlla i documenti. In questo modo puoi evitare di controllare il contesto NULL.
Imtiaz Shakil Siddique

14

Puoi implementare un metodo hasRole () come di seguito - (Questo è testato su Spring Security 3.0.x non sono sicuro delle altre versioni.)

  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()può recuperare null. Forse aggiungi qualche assegno?
Mrusful

10

Sto usando questo:

@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

Puoi ottenere aiuto dalla classe AuthorityUtils . Controllo del ruolo di battuta:

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

Avvertenza: questo non controlla la gerarchia dei ruoli, se esiste.


Questa è la soluzione più semplice perché devo controllare l'elenco più volte e recuperarlo una volta con una semplice routine magica è fantastico!
LeO

6

La risposta di JoseK non può essere usata quando sei nel tuo livello di servizio, dove non vuoi introdurre un accoppiamento con il livello web dal riferimento alla richiesta HTTP. Se stai cercando di risolvere i ruoli mentre sei nel livello di servizio, la risposta di Gopi è la strada da percorrere.

Tuttavia, è un po 'prolisso. È possibile accedere alle autorità direttamente dall'autenticazione. Quindi, se puoi presumere di avere un utente connesso, lo fa quanto segue:

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

Alla maggior parte delle risposte mancano alcuni punti:

  1. Il ruolo e l'autorità non sono la stessa cosa in primavera. Vedi qui per maggiori dettagli.

  2. I nomi dei ruoli sono uguali a rolePrefix+ authority.

  3. Il prefisso del ruolo predefinito è ROLE_, tuttavia, configurabile. Vedi qui .

Pertanto, un corretto controllo del ruolo deve rispettare il prefisso del ruolo, se configurato.

Sfortunatamente, la personalizzazione del prefisso del ruolo in Spring è un po 'hacky, in molti luoghi il prefisso predefinito ROLE_è hardcoded, ma in aggiunta a ciò, un bean di tipo GrantedAuthorityDefaultsviene controllato nel contesto Spring e, se esiste, il prefisso del ruolo personalizzato lo è rispettato.

Riunendo tutte queste informazioni, una migliore implementazione del controllo dei ruoli sarebbe qualcosa del tipo:

@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

Stranamente, non credo che esista una soluzione standard a questo problema, poiché il controllo dell'accesso alla sicurezza primaverile è basato su espressioni , non su Java. potresti controllare il codice sorgente di DefaultMethodSecurityExpressionHandler per vedere se puoi riutilizzare qualcosa che stanno facendo lì


Quindi la tua soluzione è usare DefaultMethodSecurityExpressionHandler come bean e ottenere il parser delle espressioni e controllarlo in EL?
Piotr Gwiazda,

probabilmente non funzionerà, poiché il gestore opera su invocazioni di metodo (che non hai nel tuo contesto). probabilmente hai bisogno di creare il tuo bean che fa qualcosa di simile, ma senza utilizzare un contesto di invocazione del metodo
Sean Patrick Floyd,

2

Meglio tardi che mai, lasciami mettere i miei 2 centesimi.

Nel mondo JSF, all'interno del mio bean gestito, ho fatto quanto segue:


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

Come accennato in precedenza, la mia comprensione è che può essere fatto nel modo più lungo come segue:


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

2

Questa è una specie di domanda dall'altra parte, ma ho pensato di inserirla perché dovevo davvero scavare su Internet per scoprirlo.

Ci sono molte cose su come controllare i ruoli ma non c'è molto da dire su ciò che stai effettivamente controllando quando dici hasRole ("blah")

HasRole controlla le autorità concesse per l'entità attualmente autenticata

Quindi, in realtà, quando vedi hasRole ("blah") significa davvero hasAuthority ("blah") .

Nel caso che ho visto, lo fai con una classe che implementa UserDetails che definisce un metodo chiamato getAuthorities. In questo fondamentalmente ne aggiungerai alcuni new SimpleGrantedAuthority("some name")a un elenco basato su una logica. I nomi in questo elenco sono le cose controllate dalle istruzioni hasRole.

Immagino che in questo contesto l'oggetto UserDetails sia l'entità attualmente autenticata. C'è qualche magia che accade dentro e intorno ai provider di autenticazione e più specificamente al gestore di autenticazione che lo fa accadere.


2
A partire da Spring Security 4.0 questo hasRole("bla")è ora uguale a hasAuthority("ROLE_bla").
lanoxx

2

La risposta di @gouki è la migliore!

Solo un suggerimento su come la primavera fa davvero questo.

C'è una classe denominata SecurityContextHolderAwareRequestWrapperche implementa la ServletRequestWrapperclasse.

L' SecurityContextHolderAwareRequestWrapperoverride di isUserInRolee cerca utente Authentication(che è gestito da Spring) per trovare se l'utente ha un ruolo o meno.

SecurityContextHolderAwareRequestWrapper il codice è come:

    @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

Le due annotazioni seguenti sono uguali, "hasRole" aggiungerà automaticamente il prefisso "ROLE_". Assicurati di avere l'annotazione giusta. Questo ruolo è impostato in UserDetailsService # loadUserByUsername.

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

quindi, puoi ottenere il ruolo nel codice java.

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

1

Nel nostro progetto, stiamo usando una gerarchia di ruoli, mentre la maggior parte delle risposte di cui sopra mirano solo a controllare per un ruolo specifico, cioè verrebbero verificate solo per il ruolo dato, ma non per quel ruolo e in alto nella gerarchia.

Una soluzione per questo:

@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 è definito come un bean in spring-security.xml.


1
Oppure puoi popolare correttamente i tuoi ruoli: github.com/spring-projects/spring-security/issues/…
arctica

1

Sul tuo modello utente aggiungi semplicemente un metodo "hasRole" come di seguito

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

Di solito lo uso per verificare se l'utente autenticato ha il ruolo di amministratore come segue

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

I ruoli utente possono essere controllati utilizzando i seguenti modi:

  1. Utilizzo di metodi statici di chiamata in SecurityContextHolder:

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

  2. Utilizzando HttpServletRequest

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


0

Il mio approccio con l'aiuto di Java8, il passaggio di ruoli separati dal coma ti darà vero 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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.