Różnica między rolą a uprawnieniem przyznanym w Spring Security


227

W Spring Security istnieją koncepcje i implementacje, takie jak GrantedAuthorityinterfejs pozwalający uzyskać uprawnienia do autoryzacji / kontroli dostępu.

Chciałbym, aby dozwolone operacje, takie jak createSubUsers lub deleteAccounts , które pozwoliłbym administratorowi (z rolą ROLE_ADMIN).

Czuję się zagubiony w tutorialach / pokazach, które widzę online. Próbuję połączyć to, co przeczytałem, ale myślę, że traktujemy te dwa zamiennie.

Widzę hasRolekonsumowanie GrantedAuthoritysznurka? Zdecydowanie źle to rozumiem. Jakie są koncepcyjnie w Spring Security?

Jak przechowywać rolę użytkownika, niezależnie od uprawnień dla tej roli?

Patrzę również na org.springframework.security.core.userdetails.UserDetailsinterfejs, który jest używany w DAO, do którego odnosi się dostawca uwierzytelnienia, który zużywa User(uwaga na ostatnią GrantedAuthority):

public User(String username, 
            String password, 
            boolean enabled, 
            boolean accountNonExpired,
            boolean credentialsNonExpired, 
            boolean accountNonLocked, 
            Collection<? extends GrantedAuthority> authorities)

Czy jest jakiś inny sposób na odróżnienie dwóch pozostałych? Czy to nie jest obsługiwane i musimy stworzyć własne?

Odpowiedzi:


365

Pomyśl o GrantedAuthority jako o „pozwoleniu” lub „prawie”. Te „uprawnienia” są (normalnie) wyrażone jako ciągi znaków (za pomocągetAuthority() metody). Te ciągi pozwalają zidentyfikować uprawnienia i pozwalają wyborcom zdecydować, czy przyznają dostęp do czegoś.

Można przyznać użytkownikom różne GrantedAuthority (uprawnienia), umieszczając je w kontekście bezpieczeństwa. Zwykle robisz to, implementując własną usługę UserDetailsService, która zwraca implementację UserDetails, która zwraca potrzebne GrantedAuthorities.

Role (jak są używane w wielu przykładach) są po prostu „uprawnieniami” z konwencją nazewnictwa, która mówi, że rolą jest GrantedAuthority, która zaczyna się od przedrostka ROLE_. Nic więcej. Rolą jest po prostu GrantedAuthority - „pozwolenie” - „prawo”. Widzisz wiele miejsc w zabezpieczeniach wiosennych, w których rola z jej ROLE_prefiksem jest obsługiwana specjalnie, jak np. W RoleVoter, gdzie ROLE_prefiks jest używany domyślnie. Pozwala to na podanie nazw ról bez ROLE_prefiksu. Przed Spring Security 4 ta specjalna obsługa „ról” nie była przestrzegana bardzo konsekwentnie, a uprawnienia i role były często traktowane tak samo (jak np. Można zobaczyć w implementacji hasAuthority()metody whasRole()). Ze sprężyną Security 4, leczenie ról jest bardziej spójna i kod, który zajmuje się „ról” (jak RoleVoterThe hasRolewyrażenie itd) zawsze dodaje ROLE_prefiks dla Ciebie. Więc hasAuthority('ROLE_ADMIN')oznacza to samo co hasRole('ADMIN')ponieważ ROLE_prefiks zostanie dodany automatycznie. Więcej informacji można znaleźć w przewodniku migracji zabezpieczeń wiosennych 3 do 4 .

Ale nadal: rola jest tylko autorytetem ze specjalnym ROLE_prefiksem. Tak więc na wiosnę bezpieczeństwo 3 @PreAuthorize("hasRole('ROLE_XYZ')")jest takie samo, @PreAuthorize("hasAuthority('ROLE_XYZ')")a na wiosnę bezpieczeństwo 4 @PreAuthorize("hasRole('XYZ')")jest takie samo jak @PreAuthorize("hasAuthority('ROLE_XYZ')").

Jeśli chodzi o przypadek użycia:

Użytkownicy mają role i role mogą wykonywać określone operacje.

Możesz skończyć GrantedAuthoritiesdla ról, do których należy użytkownik i operacji, które rola może wykonywać. GrantedAuthoritiesDla ról mają prefiks ROLE_i operacje mają prefiks OP_. Przykładem mogą być działania władz OP_DELETE_ACCOUNT, OP_CREATE_USER, OP_RUN_BATCH_JOBitp Role mogą być ROLE_ADMIN, ROLE_USER,ROLE_OWNER itd.

Może się zdarzyć, że twoje jednostki zaimplementują się GrantedAuthoritytak jak w tym (pseudo-kodzie) przykładzie:

@Entity
class Role implements GrantedAuthority {
    @Id
    private String id;

    @ManyToMany
    private final List<Operation> allowedOperations = new ArrayList<>();

    @Override
    public String getAuthority() {
        return id;
    }

    public Collection<GrantedAuthority> getAllowedOperations() {
        return allowedOperations;
    }
}

@Entity
class User {
    @Id
    private String id;

    @ManyToMany
    private final List<Role> roles = new ArrayList<>();

    public Collection<Role> getRoles() {
        return roles;
    }
}

@Entity
class Operation implements GrantedAuthority {
    @Id
    private String id;

    @Override
    public String getAuthority() {
        return id;
    }
}

Identyfikatory ról i operacji tworzonych w bazie danych będzie reprezentacja GrantedAuthority, np ROLE_ADMIN,OP_DELETE_ACCOUNT itd. Gdy użytkownik jest uwierzytelniony, upewnij się, że wszystkie GrantedAuthorities wszystkich swoich ról i odpowiadające im operacje są zwracane z UserDetails.getAuthorities () metoda.

Przykład: Administrator rolę id ROLE_ADMINma operacje OP_DELETE_ACCOUNT, OP_READ_ACCOUNT, OP_RUN_BATCH_JOBprzypisane do niego. Rola użytkownika o identyfikatorze ROLE_USERma operacjęOP_READ_ACCOUNT .

Jeśli loguje admin w otrzymanej kontekście zabezpieczeń będzie mieć GrantedAuthorities: ROLE_ADMIN, OP_DELETE_ACCOUNT, OP_READ_ACCOUNT,OP_RUN_BATCH_JOB

Jeśli użytkownik loguje się, będzie to mieć ROLE_USER,OP_READ_ACCOUNT

UserDetailsService zadbałby o zebranie wszystkich ról i wszystkich operacji tych ról i udostępnienie ich za pomocą metody getAuthorities () w zwróconej instancji UserDetails.


2
Dziękuję Ci! Wszędzie szukałem, dlaczego „hasRole ('rolename') 'nie działa w Spring 4 -> Ich dokumentacja nie była tak łatwa do przejrzenia. Po prostu szybkie „znajdź i zamień” i wracam na właściwe tory!
Jørgen Skår Fischer

12
To świetna odpowiedź. Jedną rzeczą, aby jasno wyjaśnić trochę to inaczej, hasRole('xyz')w Wiosna Security 4 spodziewa się, aby mieć ROLE_ prefiks natomiast hasAuthority('xyz')nie oczekuje prefiks i ocenia dokładnie to, co jest przekazywane w. Użyłem tego rozwiązania, ale został uruchomiony na problemy z hasRole('OP_MY_PERMISSION')od ROLE_ była potrzebna prefiks. Zamiast tego powinienem był użyć hasAuthority('OP_MY_PERMISSION')skoro nie miałem przedrostka.
randal4

@james, możesz spojrzeć na to pytanie stackoverflow.com/questions/41500031/…
saurabh kumar

1
W JSTL springframework.org/security/tags <sec:authorize access="hasRole('ADMIN')">jest taki sam jak<sec:authorize access="hasRole('ROLE_ADMIN')">
Alex78191

1
Tak. OP_ to tylko dowolny prefiks. ROLE_ ma szczególne znaczenie w niektórych implementacjach wiosennych.
James

9

AFAIK GrantedAuthority i role są takie same w wiosennym bezpieczeństwie. Rolą jest łańcuch getAuthority () GrantedAuthority (zgodnie z domyślną implementacją SimpleGrantedAuthority).

W twoim przypadku możesz użyć hierarchicznych ról

<bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
    <constructor-arg ref="roleHierarchy" />
</bean>
<bean id="roleHierarchy"
        class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
    <property name="hierarchy">
        <value>
            ROLE_ADMIN > ROLE_createSubUsers
            ROLE_ADMIN > ROLE_deleteAccounts 
            ROLE_USER > ROLE_viewAccounts
        </value>
    </property>
</bean>

Nie dokładnie taki zol, którego szukasz, ale mam nadzieję, że to pomoże

Edytować : Odpowiedz na swój komentarz

Rola jest jak zezwolenie na wiosenne bezpieczeństwo. użycie intercept-url z hasRole zapewnia bardzo precyzyjną kontrolę nad tym, która operacja jest dozwolona dla jakiej roli / uprawnienia.

Sposób, w jaki obsługujemy naszą aplikację, definiujemy uprawnienia (tj. Rolę) dla każdej operacji (lub adresu URL) dla np. Konta_wyświetlania, konta_skasowania, konta_dodatkowego itp. Następnie tworzymy profile logiczne dla każdego użytkownika, np. Admin, użytkownik_użytkownika, użytkownik_zwykły. Profile to logiczne grupowanie uprawnień, niezależne od zabezpieczenia wiosennego. Po dodaniu nowego użytkownika zostaje do niego przypisany profil (posiadający wszystkie dozwolone uprawnienia). Teraz, gdy użytkownik próbuje wykonać jakąś akcję, uprawnienia / rola dla tej akcji są sprawdzane pod kątem przyznanego przez użytkownika uprawnienia.

Również domyślny RoleVoter używa przedrostka ROLE_, więc każdy organ zaczynający się od ROLE_ jest uważany za rolę, możesz zmienić to domyślne zachowanie, używając niestandardowego prefiksu RoleP w roli wyborca ​​roli i używając go w zabezpieczeniach wiosennych.


1
Dziękuję za pomoc Hierarchia wygląda na coś innego. Sposób, w jaki myślę o zrobieniu tego teraz (jeśli muszę użyć Spring Security), polega na zapisaniu roli i uprawnień na tej samej liście i użyciu predykatu hasRole do wykonania obu kontroli. Zastanawiasz się nad tym - chłopaki z Spring Security prawdopodobnie pozostawili to celowo? Czy istnieje możliwość użycia adresu URL przechwytywania innego niż sprawdzanie przy użyciu hasRole na pełnej liście ról ORAZ uprawnień / organów - lub innych aplikacji autoryzacyjnych. Jaka jest potrzeba prefiksu ROLE_? Czy to konwencja?
Chinmay

7

Innym sposobem na zrozumienie związku między tymi pojęciami jest interpretacja ROLI jako kontenera organów.

Organy są drobiazgowymi uprawnieniami ukierunkowanymi na określone działanie połączone czasami z określonym zakresem danych lub kontekstem. Na przykład odczyt, zapis, zarządzanie mogą reprezentować różne poziomy uprawnień do danego zakresu informacji.

Ponadto uprawnienia są egzekwowane głęboko w przepływie przetwarzania żądania, podczas gdy rola jest filtrowana według filtru żądania przed dotarciem do kontrolera. Najlepsze praktyki zalecają wdrożenie organów ścigania poza Administratorem w warstwie biznesowej.

Z drugiej strony ROLE to gruboziarnista reprezentacja zestawu uprawnień. ROLE_READER miałby tylko uprawnienia do odczytu lub przeglądania, podczas gdy ROLE_EDITOR miałby zarówno odczyt, jak i zapis. Role są używane głównie do pierwszego przeglądu na obrzeżach przetwarzania żądania, takiego jak http. ... .antMatcher (...). hasRole (ROLE_MANAGER)

Egzekwowanie uprawnień przez władze w procesie przetwarzania wniosku pozwala na bardziej szczegółowe zastosowanie pozwolenia. Na przykład użytkownik może mieć uprawnienia do odczytu Zapis do pierwszego poziomu zasobu, ale tylko do odczytu do podrzędnego zasobu. Posiadanie ROLE_READER ograniczyłoby jego prawo do edytowania zasobu pierwszego poziomu, ponieważ potrzebuje on uprawnienia do zapisu do edycji tego zasobu, ale przechwytywacz @PreAuthorize może zablokować jego próbę edycji podrzędnego zasobu.

Jake


2

Jak wspomnieli inni, uważam role za kontenery dla bardziej szczegółowych uprawnień.

Chociaż zauważyłem, że implementacja roli hierarchii nie ma dokładnej kontroli nad tymi szczegółowymi uprawnieniami.
Stworzyłem więc bibliotekę do zarządzania relacjami i wstrzykiwania uprawnień jako nadane uprawnienia w kontekście bezpieczeństwa.

Mogę mieć zestaw uprawnień w aplikacji, np. UTWÓRZ, ODCZYTAJ, AKTUALIZUJ, USUŃ, które są następnie powiązane z rolą użytkownika.

Lub bardziej szczegółowe uprawnienia, takie jak READ_POST, READ_PUBLISHED_POST, CREATE_POST, PUBLISH_POST

Uprawnienia te są względnie statyczne, ale relacja ról z nimi może być dynamiczna.

Przykład -

@Autowired 
RolePermissionsRepository repository;

public void setup(){
  String roleName = "ROLE_ADMIN";
  List<String> permissions = new ArrayList<String>();
  permissions.add("CREATE");
  permissions.add("READ");
  permissions.add("UPDATE");
  permissions.add("DELETE");
  repository.save(new RolePermissions(roleName, permissions));
}

Możesz tworzyć interfejsy API do zarządzania relacją tych uprawnień do roli.

Nie chcę kopiować / wklejać innej odpowiedzi, więc oto link do pełniejszego wyjaśnienia na temat SO.
https://stackoverflow.com/a/60251931/1308685

Aby ponownie użyć mojej implementacji, utworzyłem repozytorium. Prosimy o kontakt!
https://github.com/savantly-net/spring-role-permissions

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.