Як перевірити "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
@Autwired HttpServletRequest запит; ?
Паскаль

І це навіть не Spring API, звичайна специфікація сервлетів! Сором, що це не обрана відповідь
gregfqt

67

Замість використання циклу для пошуку повноважень з UserDetails ви можете:

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

2
Набагато приємніша відповідь, проте ROLE_ADMIN має бути в подвійних лапках.
Еріка Кейн

6
Це дуже ризиковано. Майте на увазі, що перехід на іншу реалізацію реалізації GrantedAuthority (наприклад, JAAS, додавши іншу можливість авторизації) призведе до несправності цього коду. Дивіться реалізацію рівнянь () у SimpleGrantedAuthority
Петро Újezdský

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 (), як показано нижче - (Це перевірено на захищеності версії 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. Можливо, ви додасте чек?
Грізний

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

Caveat: Це не перевіряє ієрархію ролей, якщо така існує.


Це найпростіше рішення, тому що мені потрібно перевірити Список кілька разів, і витягнути його один раз за допомогою якоїсь магічної простої програми - це чудово!
LeO

6

Відповідь від 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;
}

6

У більшості відповідей відсутні деякі моменти:

  1. Роль та влада - це не одне і те ж, що весна. Дивіться тут для більш детальної інформації.

  2. Назви ролей дорівнюють rolePrefix+ authority.

  3. Префікс ролі за замовчуванням ROLE_, однак, він може бути налаштований. Дивіться тут .

Тому для правильної перевірки ролі потрібно дотримуватися префікса ролі, якщо він налаштований.

На жаль, налаштування префіксу ролей у Spring є трохи хитким, у багатьох місцях префікс за замовчуванням ROLE_є жорстким кодом, але на додаток до цього, bean типу 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

Як не дивно, я не думаю, що існує стандартне рішення цієї проблеми, оскільки пружинний контроль доступу ґрунтується на виразі , а не на основі Java. ви можете перевірити вихідний код для DefaultMethodSecurityExpressionHandler, щоб побачити, чи можете ви повторно використовувати те, що вони роблять там


Отже, ваше рішення полягає в тому, щоб використовувати DefaultMethodSecurityExpressionHandler як bean і отримати аналізатор вираження і перевірити його в EL?
Piotr Gwiazda

це, ймовірно, не буде працювати, оскільки обробник працює над методами викликів (яких у вашому контексті немає). вам, ймовірно, потрібно створити власний боб, який робить щось подібне, але без використання контексту методу викликання
Шон Патрік Флойд

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 ("blah"), справді означає hasAuthority ("blah") .

У випадку, який я бачив, ви робите це з класом, який реалізує 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 визначається як боб у spring-security.xml.


1
Або ви можете правильно заповнити свої ролі: github.com/spring-projects/spring-security/isissue/…
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.