Como verificar “hasRole” no código Java com Spring Security?

118

Como verificar a autoridade ou permissão do usuário no código Java? Por exemplo - desejo mostrar ou ocultar o botão para o usuário, dependendo da função. Existem anotações como:

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

Como fazer em código Java? Algo como :

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

Respostas:

70

Spring Security 3.0 tem esta API

SecurityContextHolderAwareRequestWrapper.isUserInRole(String role)

Você terá que injetar o invólucro, antes de usá-lo.

SecurityContextHolderAwareRequestWrapper

JoseK
fonte
53
Conforme sua resposta é declarada, parece que o método é estático, mas você precisa de uma SecurityContextHolderAwareRequestWrapperinstância. Você poderia melhorá-lo explicando como obtê-lo e esclarecendo um pouco mais a própria resposta.
Xtreme Biker
3
Como posso recuperar o invólucro no Controlador?
Alfonso Tienda
3
Como posso obter uma instância de SecurityContextHolderAwareRequestWrapper?
gstackoverflow de
2
O Xtreme Biker está certo, como você obtém a classe SecurityContextHolderAwareRequestWrapper? Não é um objeto estático.
Experimente
5
Se fosse um aplicativo da web, o que parece que não é, você poderia simplesmente adicionar SecurityContextHolderAwareRequestWrapper como um parâmetro. E se fosse um aplicativo da web, você poderia simplesmente declarar HttpServletRequest como um parâmetro e chamar isUserInRole
David Bradley
144

você pode usar o método isUserInRole do objeto HttpServletRequest.

algo como:

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


    if (request.isUserInRole("ROLE_ADMIN")) {
        // code here
    }
}
gouki
fonte
mais fácil de testar, eu acho
fego
1
mas se eu não solicitar?
gstackoverflow
Que tal ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest()receber o pedido? :)
Petr Újezdský
4
@Autowired HttpServletRequest request; ?
Pascal
E isso não é nem mesmo uma API Spring, especificações simples de Servlet! Uma pena que não seja a resposta escolhida
gregfqt
67

Em vez de usar um loop para encontrar a autoridade de UserDetails, você pode fazer:

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
boolean authorized = authorities.contains(new SimpleGrantedAuthority("ROLE_ADMIN"));
Ninca
fonte
2
Uma resposta muito mais agradável, no entanto, o ROLE_ADMIN deve estar entre aspas duplas.
Erica Kane
6
Isso é muito arriscado. Esteja ciente de que mudar para outra implementação da implementação GrantedAuthority (por exemplo, JAAS, adicionando outra possibilidade de autorização) fará com que este código funcione incorretamente. Consulte a implementação de equals () em SimpleGrantedAuthority
Petr Újezdský de
47

Você pode recuperar o contexto de segurança e usá-lo:

    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
fonte
SecurityContextHolder.getContext()é nunca NULL, verifique os documentos. Assim, você pode evitar a verificação do contexto NULL.
Imtiaz Shakil Siddique
14

Você pode implementar um método hasRole () conforme abaixo - (testado no spring security 3.0.x não tenho certeza sobre outras versões).

  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
fonte
1
SecurityContextHolder.getContext().getAuthentication()pode recuperar null. Talvez você adicione algum cheque?
Mrusful de
10

Estou usando este:

@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
fonte
8

Você pode obter ajuda na classe AuthorityUtils . Verificando a função de uma linha:

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

Advertência: isso não verifica a hierarquia de papéis, se houver.

holmis83
fonte
Essa é a solução mais simples, pois preciso verificar a lista várias vezes e recuperá-la uma vez com alguma rotina mágica simples é ótimo!
LeO
6

A resposta de JoseK não pode ser usada quando você está em sua camada de serviço, onde você não deseja introduzir um acoplamento com a camada da web a partir da referência à solicitação HTTP. Se você está procurando resolver as funções enquanto estiver na camada de serviço, a resposta de Gopi é o caminho a percorrer.

No entanto, é um pouco prolixo. As autoridades podem ser acessadas diretamente da Autenticação. Portanto, se você pode presumir que tem um usuário conectado, o seguinte o fará:

/**
 * @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;
}
Homem das Estrelas
fonte
6

A maioria das respostas não contém alguns pontos:

  1. Papel e autoridade não são a mesma coisa na primavera. Veja aqui para mais detalhes.

  2. Os nomes das funções são iguais a rolePrefix+ authority.

  3. O prefixo de função padrão é ROLE_, entretanto, configurável. Veja aqui .

Portanto, uma verificação de função adequada precisa respeitar o prefixo da função, se estiver configurado.

Infelizmente, a personalização do prefixo de função no Spring é um pouco hacky, em muitos lugares o prefixo padrão, ROLE_é codificado, mas, além disso, um bean do tipoGrantedAuthorityDefaults é verificado no contexto do Spring e, se existir, o prefixo de função personalizado é tem é respeitado.

Reunindo todas essas informações, uma implementação melhor do verificador de função seria 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
fonte
3

Estranhamente, não acho que haja uma solução padrão para esse problema, já que o controle de acesso spring-security é baseado em expressão , não em java. você pode verificar o código-fonte de DefaultMethodSecurityExpressionHandler para ver se você pode reutilizar algo que eles estão fazendo lá

Sean Patrick Floyd
fonte
Portanto, sua solução é usar DefaultMethodSecurityExpressionHandler como bean e obter o analisador de expressão e verificá-lo em EL?
Piotr Gwiazda
isso provavelmente não funcionará, visto que o manipulador opera em invocações de método (que você não tem em seu contexto). você provavelmente precisa criar seu próprio bean que faz algo semelhante, mas sem usar um contexto de método de invocação
Sean Patrick Floyd
2

Melhor tarde do que nunca, deixe-me investir meu valor de 2 centavos.

No mundo JSF, dentro do meu bean gerenciado, fiz o seguinte:


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

Conforme mencionado acima, meu entendimento é que isso pode ser feito da maneira mais árdua da seguinte forma:


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

Esta é uma espécie de pergunta vinda do outro lado, mas pensei em jogar porque eu realmente tinha que cavar na internet para descobrir isso.

Há muitas coisas sobre como verificar papéis, mas não há muito que dizer o que você está realmente verificando quando diz hasRole ("blá")

HasRole verifica as autoridades concedidas para o principal atualmente autenticado

Então, realmente, quando você vê hasRole ("blah"), realmente significa hasAuthority ("blah") .

No caso que vi, você faz isso com uma classe que implementa UserDetails, que define um método chamado getAuthorities. Nisto, você basicamente adicionará algunsnew SimpleGrantedAuthority("some name") a uma lista com base em alguma lógica. Os nomes nesta lista são os itens verificados pelas instruções hasRole.

Acho que, neste contexto, o objeto UserDetails é o principal atualmente autenticado. Existe alguma mágica que acontece dentro e ao redor dos provedores de autenticação e, mais especificamente, do gerenciador de autenticação que faz isso acontecer.

JonnyRaa
fonte
2
No Spring Security 4.0, isso hasRole("bla")agora é igual a hasAuthority("ROLE_bla").
lanoxx
2

A resposta @gouki é a melhor!

Apenas uma dica de como a primavera realmente faz isso.

Existe uma classe chamada SecurityContextHolderAwareRequestWrapperque implementa oServletRequestWrapper classe.

O SecurityContextHolderAwareRequestWrappersubstitui o isUserInRoleusuário e search Authentication(que é gerenciado pelo Spring) para descobrir se o usuário tem uma função ou não.

SecurityContextHolderAwareRequestWrapper o código é 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
fonte
2

Essas duas anotações abaixo são iguais, "hasRole" adicionará automaticamente o prefixo "ROLE_". Certifique-se de ter a anotação correta. Esta função é definida em UserDetailsService # loadUserByUsername.

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

então, você pode obter a função no código java.

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

Em nosso projeto, estamos usando uma hierarquia de papéis, enquanto a maioria das respostas acima visa apenas a verificação de um papel específico, ou seja, verificaria apenas o papel fornecido, mas não para aquele papel e subir na hierarquia.

Uma solução para isso:

@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 é definido como um bean em spring-security.xml.

Kirinya
fonte
1
Ou você pode preencher suas funções corretamente: github.com/spring-projects/spring-security/issues/…
arctica
1

Em seu modelo de usuário, basta adicionar um método 'hasRole' como abaixo

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

Eu geralmente uso para verificar se o usuário autenticado tem a função admin da seguinte maneira

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

As funções do usuário podem ser verificadas das seguintes maneiras:

  1. Usando métodos estáticos de chamada em 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")) {
      
    }

Por sorte
fonte
0

Minha abordagem com a ajuda de Java8, passando por papéis separados por vírgulas lhe dará verdadeiro ou 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
fonte