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


118

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

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

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

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

Ответы:


70

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

SecurityContextHolderAwareRequestWrapper.isUserInRole(String role)

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

SecurityContextHolderAwareRequestWrapper


53
Как указано в вашем ответе, похоже, что метод статический, однако вам нужен SecurityContextHolderAwareRequestWrapperэкземпляр. Вы можете улучшить его, объяснив, как его получить, и немного пояснив сам ответ.
Xtreme Biker

3
Как я могу получить оболочку в контроллере?
Альфонсо Тиенда

3
Как я могу получить экземпляр SecurityContextHolderAwareRequestWrapper?
gstackoverflow

2
Xtreme Biker прав, как получить класс SecurityContextHolderAwareRequestWrapper? Это не статический объект.
Попробуйте

5
Если бы это было веб-приложение, а похоже, что это не так, вы могли бы просто добавить SecurityContextHolderAwareRequestWrapper в качестве параметра. И если бы это было веб-приложение, вы могли бы просто объявить HttpServletRequest в качестве параметра и вызвать isUserInRole
Дэвид Брэдли,

144

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

что-то вроде:

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


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

легче проверить я думаю
fego

1
а если у меня нет запроса?
gstackoverflow

Как насчет ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest()получения запроса? :)
Петр jezdský

4
@Autowired HttpServletRequest request; ?
Паскаль

И это даже не Spring API, простая спецификация сервлета! Жаль, что это не выбранный ответ
gregfqt 05

67

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

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

2
Намного более приятный ответ, однако ROLE_ADMIN должен быть в двойных кавычках.
Эрика Кейн

6
Это очень рискованно. Имейте в виду, что переключение на другую реализацию реализации GrantedAuthority (например, JAAS, путем добавления другой возможности авторизации) приведет к сбою этого кода. См. Реализацию equals () в SimpleGrantedAuthority
Петр Уездский

47

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

    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()никогда не бывает NULL, проверьте документацию. Таким образом, вы можете избежать проверки наличия контекста NULL.
Имтиаз Шакил Сиддик,

14

Вы можете реализовать метод hasRole (), как показано ниже - (Он протестирован на Spring Security 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;
  }

1
SecurityContextHolder.getContext().getAuthentication()можно получить null. Может чек добавить?
Mrusful

10

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

@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

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

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

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


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

6

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

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

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

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

  1. Весной роль и власть - это не одно и то же. Смотрите здесь для более подробной информации.

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

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

Следовательно, при правильной проверке роли необходимо учитывать префикс роли, если он настроен.

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

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

@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

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


Итак, ваше решение - использовать DefaultMethodSecurityExpressionHandler в качестве bean-компонента, получить синтаксический анализатор выражений и проверить его в EL?
Piotr Gwiazda

это, вероятно, не сработает, так как обработчик работает с вызовами методов (которых у вас нет в вашем контексте). вам, вероятно, потребуется создать свой собственный bean-компонент, который делает что-то подобное, но без использования контекста вызова метода
Шон Патрик Флойд,

2

Лучше поздно, чем никогда, позволь мне вложить свои 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();
}

2

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

Есть много вещей о том, как проверять роли, но не так много говорится о том, что вы на самом деле проверяете, когда вы говорите hasRole ("бла")

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

Итак, когда вы видите hasRole («бла»), на самом деле означает hasAuthority («бла») .

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

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


2
Начиная с Spring Security 4.0 hasRole("bla")теперь равно hasAuthority("ROLE_bla").
lanoxx

2

Ответ @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;
    }

2

Эти две аннотации ниже равны: 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");
}

1

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

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

@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.


1
Или вы можете правильно заполнить свои роли: github.com/spring-projects/spring-security/issues/…
arctica

1

В вашей пользовательской модели просто добавьте метод 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

1

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

  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")) {
      
    }


0

Мой подход с помощью 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;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.