Jak sprawdzić „hasRole” w Java Code za pomocą Spring Security?

118

Jak sprawdzić uprawnienia lub uprawnienia użytkownika w kodzie Java? Na przykład - chcę pokazać lub ukryć przycisk dla użytkownika w zależności od roli. Istnieją adnotacje takie jak:

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

Jak to zrobić w kodzie Java? Coś jak :

if(somethingHere.hasRole("ROLE_MANAGER")) {
   layout.addComponent(new Button("Edit users"));
}
Piotr Gwiazda
źródło

Odpowiedzi:

70

Spring Security 3.0 ma ten interfejs API

SecurityContextHolderAwareRequestWrapper.isUserInRole(String role)

Będziesz musiał wstrzyknąć opakowanie, zanim go użyjesz.

SecurityContextHolderAwareRequestWrapper

JoseK
źródło
53
Jak podano w Twojej odpowiedzi, wygląda na to, że metoda jest statyczna, jednak potrzebujesz SecurityContextHolderAwareRequestWrapperinstancji. Możesz go poprawić, wyjaśniając, jak go uzyskać, i nieco dokładniej wyjaśniając samą odpowiedź.
Xtreme Biker
3
Jak mogę odzyskać opakowanie w kontrolerze?
Alfonso Tienda
3
Jak mogę uzyskać wystąpienie SecurityContextHolderAwareRequestWrapper?
gstackoverflow
2
Xtreme Biker ma rację, jak uzyskać klasę SecurityContextHolderAwareRequestWrapper? To nie jest statyczny obiekt.
Wypróbuj
5
Jeśli to była aplikacja internetowa, a wygląda na to, że nią nie jest, możesz po prostu dodać SecurityContextHolderAwareRequestWrapper jako parametr. A gdyby to była aplikacja internetowa, można po prostu zadeklarować HttpServletRequest jako parametr i wywołać isUserInRole
David Bradley
144

można użyć metody isUserInRole obiektu HttpServletRequest.

coś jak:

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


    if (request.isUserInRole("ROLE_ADMIN")) {
        // code here
    }
}
gouki
źródło
łatwiejsze do przetestowania myślę
fego
1
ale jeśli nie mam prośby?
gstackoverflow
A co ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest()z prośbą? :)
Petr Újezdský
4
@Autowired HttpServletRequest żądanie; ?
Pascal
I to nawet nie jest Spring API, zwykła specyfikacja Servlet! Szkoda, że ​​to nie wybrana odpowiedź
gregfqt
67

Zamiast używać pętli do wyszukiwania uprawnień w UserDetails, możesz:

Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
boolean authorized = authorities.contains(new SimpleGrantedAuthority("ROLE_ADMIN"));
Ninca
źródło
2
O wiele ładniejsza odpowiedź, jednak ROLE_ADMIN powinna być ujęta w podwójne cudzysłowy.
Erica Kane
6
To jest bardzo ryzykowne. Należy pamiętać, że przejście na inną implementację implementacji GrantedAuthority (np. JAAS, poprzez dodanie kolejnej możliwości autoryzacji) spowoduje nieprawidłowe działanie tego kodu. Zobacz implementację equals () w SimpleGrantedAuthority
Petr Újezdský
47

Możesz pobrać kontekst zabezpieczeń, a następnie użyć go:

    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
źródło
SecurityContextHolder.getContext()nigdy NULL, sprawdź dokumentację. W ten sposób można uniknąć sprawdzania bytu kontekstowego NULL.
Imtiaz Shakil Siddique
14

Możesz zaimplementować metodę hasRole () jak poniżej - (jest to testowane na zabezpieczeniach sprężynowych 3.0.x nie ma pewności co do innych wersji).

  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
źródło
1
SecurityContextHolder.getContext().getAuthentication()można odzyskać null. Może dodasz jakiś czek?
Mrusful
10

Używam tego:

@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
źródło
8

Możesz uzyskać pomoc od klasy AuthorityUtils . Sprawdzanie roli jako jednoliniowiec:

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

Uwaga: to nie sprawdza hierarchii ról, jeśli taka istnieje.

holmis83
źródło
To najprostsze rozwiązanie, ponieważ muszę kilkakrotnie sprawdzić Listę i pobranie jej raz za pomocą jakiejś magicznej prostej procedury jest świetne!
LeO
6

Odpowiedź od JoseK nie może zostać użyta, gdy jesteś w warstwie usług, gdzie nie chcesz wprowadzać sprzężenia z warstwą internetową z odniesienia do żądania HTTP. Jeśli chcesz rozwiązać role w warstwie usług, odpowiedzią Gopi jest droga do zrobienia.

Jednak jest trochę rozwlekły. Dostęp do władz można uzyskać bezpośrednio z poziomu uwierzytelniania. Dlatego jeśli możesz założyć, że masz zalogowanego użytkownika, wykonaj następujące czynności:

/**
 * @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;
}
Gwiezdny człowiek
źródło
6

W większości odpowiedzi brakuje niektórych punktów:

  1. Rola i autorytet to nie to samo na wiosnę. Więcej informacji znajdziesz tutaj .

  2. Nazwy ról są równe rolePrefix+ authority.

  3. Domyślny prefiks roli można ROLE_jednak konfigurować. Zobacz tutaj .

Dlatego prawidłowe sprawdzenie roli musi uwzględniać prefiks roli, jeśli jest skonfigurowany.

Niestety, dostosowywanie prefiksów ról w Spring jest trochę hakerskie, w wielu miejscach domyślny prefiks ROLE_jest zakodowany na stałe, ale oprócz tego fasola typu GrantedAuthorityDefaultsjest sprawdzana w kontekście Spring, a jeśli istnieje, to przedrostek roli niestandardowej jest szanowany.

Łącząc wszystkie te informacje, lepszą implementacją narzędzia do sprawdzania ról byłoby coś takiego:

@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
źródło
3

Co dziwne, nie sądzę, aby istniało standardowe rozwiązanie tego problemu, ponieważ kontrola dostępu zabezpieczeń sprężynowych jest oparta na wyrażeniach , a nie w Javie. możesz sprawdzić kod źródłowy DefaultMethodSecurityExpressionHandler, aby zobaczyć, czy możesz ponownie użyć czegoś, co tam robią

Sean Patrick Floyd
źródło
Więc twoim rozwiązaniem jest użycie DefaultMethodSecurityExpressionHandler jako fasoli i pobranie parsera wyrażeń i sprawdzenie go w EL?
Piotr Gwiazda
to prawdopodobnie nie zadziała, ponieważ program obsługi operuje na wywołaniach metod (których nie masz w swoim kontekście). prawdopodobnie musisz stworzyć własną fasolę, która robi coś podobnego, ale bez użycia kontekstu przywołania metody
Sean Patrick Floyd
2

Lepiej późno, niż nigdy, pozwól, że wrzucę moje 2 centy.

W świecie JSF, w ramach mojej zarządzanej fasoli, wykonałem następujące czynności:


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

Jak wspomniano powyżej, rozumiem, że można to zrobić w długi, zawiły sposób:


Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserDetails userDetails = null;
if (principal instanceof UserDetails) {
    userDetails = (UserDetails) principal;
    Collection  authorities = userDetails.getAuthorities();
}
lukec
źródło
2

To trochę zbliża się do pytania z drugiej strony, ale pomyślałem, że dorzuciłem je, ponieważ naprawdę musiałem kopać w Internecie, aby się tego dowiedzieć.

Jest wiele rzeczy na temat sprawdzania ról, ale niewiele mówi się o tym, co sprawdzasz, kiedy mówisz ma rolę („bla”)

HasRole sprawdza przyznane uprawnienia dla aktualnie uwierzytelnionego podmiotu zabezpieczeń

Więc naprawdę, gdy widzisz, że maRole („bla”) naprawdę oznacza mieć autorytet („bla”) .

W przypadku, który widziałem, robisz to z klasą implementującą UserDetails, która definiuje metodę o nazwie getAuthorities. W tym przypadku po prostu dodasz niektóre new SimpleGrantedAuthority("some name")do listy w oparciu o jakąś logikę. Nazwy na tej liście to rzeczy sprawdzane przez instrukcje hasRole.

Myślę, że w tym kontekście obiekt UserDetails jest obecnie uwierzytelnionym podmiotem. Istnieje pewna magia, która dzieje się w dostawcach uwierzytelniania i wokół nich, a dokładniej w menedżerze uwierzytelniania, który to umożliwia.

JonnyRaa
źródło
2
Od wersji Spring Security 4.0 hasRole("bla")jest to teraz równe hasAuthority("ROLE_bla").
lanoxx
2

Odpowiedź @gouki jest najlepsza!

Tylko wskazówka, jak naprawdę robi to wiosna.

Istnieje klasa o nazwie, SecurityContextHolderAwareRequestWrapperktóra implementuje ServletRequestWrapperklasę.

Do SecurityContextHolderAwareRequestWrapperprzesłania isUserInRolei łatwość wyszukiwania Authentication(który jest zarządzany przez wiosnę), aby znaleźć jeśli użytkownik ma swoją rolę, czy nie.

SecurityContextHolderAwareRequestWrapper kod jest następujący:

    @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
źródło
2

Te dwie poniższe adnotacje są równe, „hasRole” automatycznie doda przedrostek „ROLE_”. Upewnij się, że masz właściwą adnotację. Ta rola jest ustawiana w UserDetailsService # loadUserByUsername.

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

następnie możesz uzyskać rolę w kodzie java.

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_user"))){
    System.out.println("user role2");
}
Luke Cheung
źródło
1

W naszym projekcie używamy hierarchii ról, podczas gdy większość powyższych odpowiedzi ma na celu jedynie sprawdzenie określonej roli, tj. Sprawdzenie tylko podanej roli, ale nie tej roli i wyżej w hierarchii.

Rozwiązanie tego problemu:

@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 jest zdefiniowana jako fasola w spring-security.xml.

Kirinya
źródło
1
Lub możesz poprawnie wypełnić swoje role: github.com/spring-projects/spring-security/issues/ ...
arctica
1

W swoim modelu użytkownika po prostu dodaj metodę „hasRole”, jak poniżej

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

Zwykle używam go do sprawdzenia, czy uwierzytelniony użytkownik ma rolę administratora w następujący sposób

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
źródło
1

Role użytkowników można sprawdzić w następujący sposób:

  1. Używanie metod statycznych wywołania w SecurityContextHolder:

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

  2. Korzystanie z HttpServletRequest

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

Szczęściarz
źródło
0

My Approach z pomocą Java8, Przekazywanie ról oddzielonych przecinkami da ci prawdę lub fałsz

    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
źródło