Как проверить 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
    }
}
Gouki
источник
легче проверить я думаю
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"));
Ninca
источник
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")) {
    /* ... */
}

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

holmis83
источник
Это простейшее решение, потому что мне нужно проверять список несколько раз, и получить его один раз с помощью какой-то простой волшебной процедуры - это здорово!
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();
}
lukec
источник
2

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

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

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

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

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

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

JonnyRaa
источник
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.

Kirinya
источник
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
user8174892
источник
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;
}
Джаджикантх Пидимарла
источник