Как проверить hasRole в Java-коде с помощью Spring Security?

Как проверить полномочия или разрешения пользователя в Java-коде? Например, я хочу показать или скрыть кнопку для пользователя в зависимости от роли. Есть такие аннотации:

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

Как сделать это в коде Java? Что-то типа :

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

person Piotr Gwiazda    schedule 11.06.2010    source источник


Ответы (18)


Spring Security 3.0 имеет этот API

SecurityContextHolderAwareRequestWrapper.isUserInRole(String role)

Вам нужно будет ввести обертку, прежде чем использовать ее.

SecurityContextHolderAwareRequestWrapper

person JoseK    schedule 11.06.2010
comment
Это просто поиск для каждого по auth.getAuthorities(). Легче скопировать этот код, чем создавать или извлекать объект SecurityContextHolderAwareRequestWrapper. Я бы предпочел использовать все методы безопасности EL. Например hasPermission из ACL - person Piotr Gwiazda; 11.06.2010
comment
Как указано в вашем ответе, похоже, что метод статичен, однако вам нужен экземпляр SecurityContextHolderAwareRequestWrapper. Вы можете улучшить его, объяснив, как его получить, и немного уточнив сам ответ. - person Xtreme Biker; 23.02.2015
comment
Как я могу получить обертку в контроллере? - person Alfonso Tienda; 04.08.2015
comment
Как я могу получить экземпляр SecurityContextHolderAwareRequestWrapper? - person gstackoverflow; 15.09.2015
comment
Xtreme Biker прав. Как получить класс SecurityContextHolderAwareRequestWrapper? Это не статичный объект. - person Try it; 25.09.2016
comment
Если бы это было веб-приложение, что, похоже, не так, вы могли бы просто добавить SecurityContextHolderAwareRequestWrapper в качестве параметра. И если бы это было веб-приложение, вы могли бы просто объявить HttpServletRequest в качестве параметра и вызвать isUserInRole - person David Bradley; 09.09.2018
comment
Так как люди путаются в том, как использовать это. Код в контроллере выглядит так. @RequestMapping(value = "/path/to/controller") public String sampleMethod(SecurityContextHolderAwareRequestWrapper requestWrapper, Model model){ //method code requestWrapper. isUserInRole("role"); } - person Dot Batch; 31.05.2019
comment
Вам не нужно внедрять весь SecurityContextHolderAwareRequestWrapper. Использование javax.servlet.http.HttpServletRequest также будет достаточным. - person GerardV; 17.01.2021

вы можете использовать метод isUserInRole объекта HttpServletRequest.

что-то типа:

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


    if (request.isUserInRole("ROLE_ADMIN")) {
        // code here
    }
}
person gouki    schedule 23.06.2010
comment
проще проверить я думаю - person fego; 14.02.2014
comment
а если у меня нет запроса? - person gstackoverflow; 15.09.2015
comment
Как насчет ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(), чтобы получить запрос? :) - person Petr Újezdský; 19.04.2016
comment
@Autowired запрос HttpServletRequest; ? - person Pascal; 31.07.2016
comment
И это даже не Spring API, а обычная спецификация сервлета! Жаль, что это не выбранный ответ - person gregfqt; 05.03.2019

Вместо использования цикла для поиска полномочий от UserDetails вы можете сделать:

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
boolean authorized = authorities.contains(new SimpleGrantedAuthority("ROLE_ADMIN"));
person Ninca    schedule 17.09.2012
comment
Гораздо лучший ответ, однако ROLE_ADMIN должен быть в двойных кавычках. - person Erica Kane; 18.01.2015
comment
Это очень рискованно. Имейте в виду, что переключение на другую реализацию реализации GrantedAuthority (например, JAAS, путем добавления другой возможности авторизации) приведет к неправильной работе этого кода. См. реализацию equals() в SimpleGrantedAuthority. - person Petr Újezdský; 19.04.2016

Вы можете получить контекст безопасности, а затем использовать его:

    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;
    }
person Nate Sammons    schedule 19.04.2012
comment
SecurityContextHolder.getContext() никогда не бывает NULL, проверьте документы. Таким образом, вы можете избежать проверки контекста как NULL. - person Imtiaz Shakil Siddique; 07.05.2020

Вы можете реализовать метод hasRole(), как показано ниже (это проверено на весенней безопасности 3.0.x, не уверенной в других версиях).

  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;
  }
person Gopi    schedule 05.08.2010
comment
SecurityContextHolder.getContext().getAuthentication() может получить null. Может быть, вы добавить некоторые проверки? - person Mrusful; 20.01.2013

Я использую это:

@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);
}
person Joey Jarin    schedule 05.10.2011

Вы можете получить помощь от AuthorityUtils. Проверка роли в качестве однострочного:

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

Предостережение: это не проверяет иерархию ролей, если таковая существует.

person holmis83    schedule 07.08.2017
comment
Это самое простое решение, потому что мне нужно проверить список несколько раз, и получить его один раз с помощью какой-то волшебной простой процедуры — это здорово! - person LeO; 07.11.2018

В большинстве ответов отсутствуют некоторые моменты:

  1. Роль и авторитет — это не одно и то же в Spring. дополнительные сведения см. здесь.

  2. Имена ролей равны rolePrefix + authority.

  3. Префикс роли по умолчанию — ROLE_, однако его можно настроить. См. здесь.

Поэтому правильная проверка роли должна учитывать префикс роли, если он настроен.

К сожалению, настройка префикса роли в Spring немного хакерская, во многих местах префикс по умолчанию, ROLE_, жестко запрограммирован, но в дополнение к этому в контексте Spring проверяется bean-компонент типа GrantedAuthorityDefaults, и, если он существует, пользовательская роль префикс, который он имеет, соблюдается.

Собрав всю эту информацию вместе, лучшая реализация средства проверки ролей будет выглядеть примерно так:

@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);
    }
}
person Utku Özdemir    schedule 16.11.2017

Ответ от JoseK нельзя использовать, когда вы находитесь на уровне обслуживания, где вы не хотите вводить связь с веб-уровнем из ссылки на HTTP-запрос. Если вы хотите разрешить роли на уровне сервиса, ответ Gopi — это то, что вам нужно.

Тем не менее, это немного затянуто. Доступ к органам можно получить прямо из Аутентификации. Следовательно, если вы можете предположить, что у вас есть пользователь, вошедший в систему, это делает следующее:

/**
 * @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;
}
person Starman    schedule 08.10.2010

Эти две приведенные ниже аннотации равны: «hasRole» автоматически добавит префикс «ROLE_». Убедитесь, что у вас есть правильная аннотация. Эта роль задается в UserDetailsService#loadUserByUsername.

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

затем вы можете получить роль в коде Java.

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

Как ни странно, я не думаю, что существует стандартное решение этой проблемы, поскольку контроль доступа spring-security — это на основе выражений, а не на основе Java. вы можете проверить исходный код для DefaultMethodSecurityExpressionHandler, чтобы увидеть, можете ли вы повторно использовать то, что они там делают

person Sean Patrick Floyd    schedule 11.06.2010
comment
Итак, ваше решение состоит в том, чтобы использовать DefaultMethodSecurityExpressionHandler в качестве bean-компонента, получить анализатор выражений и проверить его в EL? - person Piotr Gwiazda; 11.06.2010
comment
это, вероятно, не сработает, поскольку обработчик работает с вызовами методов (которых у вас нет в вашем контексте). вам, вероятно, нужно создать свой собственный bean-компонент, который делает что-то подобное, но без использования контекста вызова метода - person Sean Patrick Floyd; 11.06.2010

Это своего рода ответ на вопрос с другого конца, но я подумал, что добавлю его, так как мне действительно пришлось копаться в Интернете, чтобы узнать это.

Существует много информации о том, как проверять роли, но мало что говорится о том, что вы на самом деле проверяете, когда говорите hasRole("blah")

HasRole проверяет предоставленные полномочия для текущего аутентифицированного принципала

Так что на самом деле, когда вы видите, что hasRole("blah") на самом деле означает hasAuthority("blah").

В случае, который я видел, вы делаете это с классом, который реализует UserDetails, который определяет метод с именем getAuthorities. В этом вы в основном добавите new SimpleGrantedAuthority("some name") в список на основе некоторой логики. Имена в этом списке — это то, что проверяется операторами hasRole.

Я предполагаю, что в этом контексте объект UserDetails является в настоящее время аутентифицированным принципалом. Есть некоторая магия, которая происходит внутри и вокруг поставщиков аутентификации и, в частности, диспетчера аутентификации, который делает это возможным.

person JonnyRaa    schedule 03.06.2014
comment
Начиная с Spring Security 4.0 этот hasRole("bla") теперь равен hasAuthority("ROLE_bla"). - person lanoxx; 28.07.2016

Лучше поздно, чем никогда, вставлю свои 2 цента.

В мире JSF в моем управляемом компоненте я сделал следующее:


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

Как упоминалось выше, я понимаю, что это можно сделать длинным путем следующим образом:


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

Ответ @gouki лучший!

Просто подсказка о том, как весна действительно делает это.

Существует класс с именем SecurityContextHolderAwareRequestWrapper, который реализует класс ServletRequestWrapper.

SecurityContextHolderAwareRequestWrapper переопределяет isUserInRole и пользователя поиска Authentication (который управляется Spring), чтобы определить, есть ли у пользователя роль или нет.

SecurityContextHolderAwareRequestWrapper код такой:

    @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;
    }
person Alireza Fattahi    schedule 08.12.2014

В нашем проекте мы используем иерархию ролей, в то время как большинство приведенных выше ответов нацелены только на проверку конкретной роли, т. е. будут проверять только заданную роль, но не эту роль и вверх по иерархии.

Решение для этого:

@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 определяется как bean-компонент в spring-security.xml.

person Kirinya    schedule 25.08.2016
comment
Или вы можете правильно заполнить свои роли: github.com/spring- проекты/весенняя безопасность/проблемы/ - person arctica; 04.08.2017

В вашей пользовательской модели просто добавьте метод hasRole, как показано ниже.

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

Обычно я использую его, чтобы проверить, имеет ли аутентифицированный пользователь роль администратора, как показано ниже.

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
person user8174892    schedule 07.08.2019

Роли пользователей можно проверить следующими способами:

  1. Использование статических методов вызова в SecurityContextHolder:

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

  2. Использование HttpServletRequest

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

person Lucky    schedule 25.07.2020

Мой подход с помощью Java8. Передача ролей, разделенных запятой, даст вам истинное или ложное

    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;
}
person Jajikanth pydimarla    schedule 19.06.2018