Inleiding tot Spring Security ACL

1. Inleiding

Toegangscontrole lijst (ACL) is een lijst met machtigingen die aan een object zijn gekoppeld. Een ACL specificeert welke identiteiten welke bewerkingen op een bepaald object worden toegekend.

Lente beveiliging Toegangscontrole lijstis een Voorjaar component die ondersteunt Domeinobjectbeveiliging. Simpel gezegd helpt Spring ACL bij het definiëren van machtigingen voor een specifieke gebruiker / rol op een enkel domeinobject - in plaats van over de hele linie, op het typische niveau per bewerking.

Bijvoorbeeld een gebruiker met de rol beheerder kan zien (LEZEN) en bewerken (SCHRIJVEN) alle berichten op een Centrale mededelingenbus, maar de normale gebruiker kan alleen berichten zien, zich ermee identificeren en kan niet bewerken. Ondertussen gebruiken anderen de rol Editor kan enkele specifieke berichten zien en bewerken.

Daarom hebben verschillende gebruikers / rollen verschillende machtigingen voor elk specifiek object. In dit geval, Lente ACL is in staat om de taak te volbrengen. We zullen onderzoeken hoe u het controleren van basistoestemmingen kunt instellen met Lente ACL in dit artikel.

2. Configuratie

2.1. ACL-database

Gebruiken Spring Security ACL, moeten we vier verplichte tabellen in onze database maken.

De eerste tafel is ACL_CLASS, welke opslagklassenaam van het domeinobject, kolommen bevatten:

  • ID kaart
  • KLASSE: de klassenaam van beveiligde domeinobjecten, bijvoorbeeld:com.baeldung.acl.persistence.entity.NoticeMessage

Ten tweede hebben we de ACL_SID tabel waarmee we elk principe of elke autoriteit in het systeem universeel kunnen identificeren. De tafel heeft nodig:

  • ID kaart
  • SID: dat is de gebruikersnaam of rolnaam. SID betekent Beveiligings-identiteit
  • OPDRACHTGEVER: 0 of 1, om aan te geven dat de overeenkomstige SID is een principal (gebruiker, zoals mary, mike, jack ...) of een autoriteit (rol, zoals ROLE_ADMIN, ROLE_USER, ROLE_EDITOR…)

De volgende tafel is ACL_OBJECT_IDENTITY, die informatie opslaat voor elk uniek domeinobject:

  • ID kaart
  • OBJECT_ID_CLASS: definieer de domeinobjectklasse,links naar ACL_CLASS tafel
  • OBJECT_ID_IDENTITY: domeinobjecten kunnen in veel tabellen worden opgeslagen, afhankelijk van de klasse. Daarom slaat dit veld de primaire sleutel van het doelobject op
  • PARENT_OBJECT: specificeer de ouder hiervan Objectidentiteit binnen deze tabel
  • OWNER_SID: ID kaart van de objecteigenaar, linkt naar ACL_SID tafel
  • ENTRIES_INHERITTING: of ACL-vermeldingen van dit object erft van het bovenliggende object (ACL-vermeldingen zijn gedefinieerd in ACL_ENTRY tafel)

eindelijk, de ACL_ENTRY sla individuele toestemming op die aan elk is toegewezen SID op een Objectidentiteit:

  • ID kaart
  • ACL_OBJECT_IDENTITY: specificeer de objectidentiteit, links naar ACL_OBJECT_IDENTITY tafel
  • ACE_ORDER: de volgorde van de huidige invoer in het ACL-vermeldingen lijst met corresponderende Objectidentiteit
  • SID: het doelwit SID waarnaar de toestemming wordt verleend of geweigerd, koppelingen naar ACL_SID tafel
  • MASKER: het integer-bitmasker dat de feitelijke toestemming vertegenwoordigt die wordt verleend of geweigerd
  • VERLENING: waarde 1 betekent toekennen, waarde 0 betekent ontkennen
  • AUDIT_SUCCESS en AUDIT_FAILURE: voor controledoeleinden

2.2. Afhankelijkheid

Om te kunnen gebruiken Lente ACL Laten we in ons project eerst onze afhankelijkheden definiëren:

 org.springframework.security spring-security-acl org.springframework.security spring-security-config org.springframework spring-context-support net.sf.ehcache ehcache-core 2.6.11 

Lente ACL vereist een cache om op te slaan Objectidentiteit en ACL-vermeldingen, dus we zullen er gebruik van maken Ehcache hier. En om te ondersteunen Ehcache in Voorjaar, we hebben ook de spring-context-ondersteuning.

Als we niet met Spring Boot werken, moeten we expliciet versies toevoegen. Die kunnen worden gecontroleerd op Maven Central: spring-security-acl, spring-security-config, spring-context-support, ehcache-core.

2.3. ACL-gerelateerde configuratie

We moeten alle methoden die beveiligde domeinobjecten retourneren, of wijzigingen in het object aanbrengen, beveiligen door in te schakelen Wereldwijde methodebeveiliging:

@Configuration @EnableGlobalMethodSecurity (prePostEnabled = true, secureEnabled = true) openbare klasse AclMethodSecurityConfiguration breidt GlobalMethodSecurityConfiguration uit {@Autowired MethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler; @Override beschermde MethodSecurityExpressionHandler createExpressionHandler () {return defaultMethodSecurityExpressionHandler; }}

Laten we ook inschakelen Expressie-gebaseerde toegangscontrole door in te stellen prePostEnabled naar waar gebruiken Spring Expression Language (SpEL). Bovendien, we hebben een expressie-handler nodig met ACL ondersteuning:

@Bean openbare MethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler () {DefaultMethodSecurityExpressionHandler expressionHandler = nieuwe DefaultMethodSecurityExpressionHandler (); AclPermissionEvaluator permissieEvaluator = nieuwe AclPermissionEvaluator (aclService ()); expressionHandler.setPermissionEvaluator (permissieEvaluator); return expressionHandler; }

Vandaar, wij wijzen AclPermissionEvaluator naar de DefaultMethodSecurityExpressionHandler. De evaluator heeft een MutableAclService om machtigingsinstellingen en definities van domeinobjecten uit de database te laden.

Voor de eenvoud gebruiken we de meegeleverde JdbcMutableAclService:

@Bean openbare JdbcMutableAclService aclService () {retourneer nieuwe JdbcMutableAclService (dataSource, lookupStrategy (), aclCache ()); }

Zoals de naam, de JdbcMutableAclService toepassingen JDBC-sjabloon om databasetoegang te vereenvoudigen. Het heeft een Databron (voor JDBC-sjabloon), LookupStrategy (biedt een geoptimaliseerde zoekactie bij het doorzoeken van de database), en een AclCache (caching ACLInzendingen en Objectidentiteit).

Nogmaals, voor de eenvoud gebruiken we voorzien BasicLookupStrategy en EhCacheBasedAclCache.

@Autowired DataSource dataSource; @Bean openbare AclAuthorizationStrategy aclAuthorizationStrategy () {retourneer nieuwe AclAuthorizationStrategyImpl (nieuwe SimpleGrantedAuthority ("ROLE_ADMIN")); } @Bean openbare PermissionGrantingStrategy permissionGrantingStrategy () {retourneer nieuwe DefaultPermissionGrantingStrategy (nieuwe ConsoleAuditLogger ()); } @Bean openbare EhCacheBasedAclCache aclCache () {retourneer nieuwe EhCacheBasedAclCache (aclEhCacheFactoryBean (). GetObject (), permantingStrategy (), aclAuthorizationStrategy ()); } @Bean openbaar EhCacheFactoryBean aclEhCacheFactoryBean () {EhCacheFactoryBean ehCacheFactoryBean = nieuwe EhCacheFactoryBean (); ehCacheFactoryBean.setCacheManager (aclCacheManager (). getObject ()); ehCacheFactoryBean.setCacheName ("aclCache"); retourneer ehCacheFactoryBean; } @Bean openbare EhCacheManagerFactoryBean aclCacheManager () {retourneer nieuwe EhCacheManagerFactoryBean (); } @Bean openbare LookupStrategy lookupStrategy () {retourneer nieuwe BasicLookupStrategy (dataSource, aclCache (), aclAuthorizationStrategy (), nieuwe ConsoleAuditLogger ()); } 

Hier de AclAuthorizationStrategy is verantwoordelijk voor de conclusie of een huidige gebruiker al dan niet over alle vereiste rechten voor bepaalde objecten beschikt.

Het heeft de steun nodig van Toestemming Verlening Strategie, die de logica definieert om te bepalen of een toestemming aan een bepaalde persoon wordt verleend SID.

3. Methode Beveiliging Met Spring ACL

Tot nu toe hebben we alle benodigde configuraties uitgevoerd. Nu kunnen we de vereiste controleregel toepassen op onze beveiligde methoden.

Standaard, Lente ACL verwijst naar BasePermission class voor alle beschikbare machtigingen. Kortom, we hebben een LEZEN, SCHRIJVEN, MAKEN, VERWIJDEREN en ADMINISTRATIE toestemming.

Laten we proberen enkele beveiligingsregels te definiëren:

@PostFilter ("hasPermission (filterObject, 'READ')") Lijst findAll (); @PostAuthorize ("hasPermission (returnObject, 'READ')") NoticeMessage findById (Integer id); @PreAuthorize ("hasPermission (#noticeMessage, 'WRITE')") NoticeMessage save (@Param ("noticeMessage") NoticeMessage noticeMessage);

Na de executie van vind alle() methode, @PostFilter wordt geactiveerd. De vereiste regel hasPermission (filterObject, ‘READ '), betekent alleen die retourneren Opmerking Bericht welke huidige gebruiker heeft LEZEN toestemming op.

Evenzo @PostAuthorize wordt geactiveerd na de uitvoering van findById () methode, zorg ervoor dat alleen de Opmerking Bericht object als de huidige gebruiker heeft LEZEN toestemming erop. Als dit niet het geval is, gooit het systeem een AccessDeniedException.

Aan de andere kant activeert het systeem de @PreAuthorize annotatie voordat u de sparen() methode. Het zal beslissen waar de overeenkomstige methode mag worden uitgevoerd of niet. Als niet, AccessDeniedException zal worden gegooid.

4. In actie

Nu gaan we al die configuraties testen met JUnit. We zullen gebruiken H2 database om de configuratie zo eenvoudig mogelijk te houden.

We moeten toevoegen:

 com.h2database h2 org.springframework spring-test org.springframework.security spring-security-test test 

4.1. Het scenario

In dit scenario hebben we twee gebruikers (manager, hr) en een één gebruikersrol (ROLE_EDITOR), zo onze acl_sid zal zijn:

INVOEGEN IN acl_sid (id, principal, sid) WAARDEN (1, 1, 'manager'), (2, 1, 'hr'), (3, 0, 'ROLE_EDITOR');

Dan moeten we aangifte doen Opmerking Bericht les in acl_class. En drie gevallen van Opmerking Bericht klasse wordt ingevoegd in systeem bericht.

Bovendien moeten overeenkomstige records voor die 3 instanties worden gedeclareerd in acl_object_identity:

INVOEGEN IN acl_class (id, class) WAARDEN (1, 'com.baeldung.acl.persistence.entity.NoticeMessage'); INSERT IN system_message (id, content) WAARDEN (1, 'First Level Message'), (2, 'Second Level Message'), (3, 'Third Level Message'); VOEG IN acl_object_identity (id, object_id_class, object_id_identity, parent_object, owner_sid, entries_inheriting) WAARDEN (1, 1, 1, NULL, 3, 0), (2, 1, 2, NULL, 3, 0), (3, 1, 3, NULL, 3, 0);

In eerste instantie verlenen we LEZEN en SCHRIJVEN machtigingen voor het eerste object (id = 1) aan de gebruiker manager. Ondertussen kan elke gebruiker met ROLE_EDITOR zal hebben LEZEN toestemming voor alle drie de objecten, maar alleen bezitten SCHRIJVEN toestemming op het derde object (id = 3). Trouwens, gebruiker u zal alleen hebben LEZEN toestemming op het tweede object.

Hier, omdat we default gebruiken Lente ACLBasePermission klasse voor machtigingscontrole, de maskerwaarde van de LEZEN toestemming is 1, en de maskerwaarde van SCHRIJVEN toestemming zal zijn 2. Onze gegevens in acl_entry zal zijn:

VOEG IN acl_entry (id, acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure) WAARDEN (1, 1, 1, 1, 1, 1, 1, 1), (2, 1, 2, 1, 2, 1, 1, 1), (3, 1, 3, 3, 1, 1, 1, 1), (4, 2, 1, 2, 1, 1, 1, 1), (5, 2, 2, 3, 1, 1, 1, 1), (6, 3, 1, 3, 1, 1, 1, 1), (7, 3, 2, 3, 2, 1, 1, 1);

4.2. Testgeval

Allereerst proberen we de vind alle methode.

Als onze configuratie retourneert de methode alleen die Opmerking Bericht waarop de gebruiker heeft LEZEN toestemming.

Daarom verwachten we dat de resultatenlijst alleen het eerste bericht bevat:

@Test @WithMockUser (gebruikersnaam = "manager") openbare ongeldige gegevenUserManager_whenFindAllMessage_thenReturnFirstMessage () {Lijstdetails = repo.findAll (); assertNotNull (details); assertEquals (1, details.size ()); assertEquals (FIRST_MESSAGE_ID, details.get (0) .getId ()); }

Vervolgens proberen we dezelfde methode aan te roepen met elke gebruiker die de rol heeft - ROLE_EDITOR. Merk op dat deze gebruikers in dit geval de LEZEN toestemming voor alle drie de objecten.

Daarom verwachten we dat de resultatenlijst alle drie de berichten zal bevatten:

@Test @WithMockUser (rollen = {"EDITOR"}) openbare leegte gegevenRoleEditor_whenFindAllMessage_thenReturn3Message () {Lijstdetails = repo.findAll (); assertNotNull (details); assertEquals (3, details.size ()); }

Gebruik vervolgens de manager gebruiker, we zullen proberen het eerste bericht op ID te krijgen en de inhoud ervan bij te werken - wat allemaal goed zou moeten werken:

@Test @WithMockUser (gebruikersnaam = "manager") openbare leegte gegevenUserManager_whenFind1stMessageByIdAndUpdateItsContent_thenOK () {NoticeMessage firstMessage = repo.findById (FIRST_MESSAGE_ID); assertNotNull (firstMessage); assertEquals (FIRST_MESSAGE_ID, firstMessage.getId ()); firstMessage.setContent (EDITTED_CONTENT); repo.save (firstMessage); NoticeMessage editFirstMessage = repo.findById (FIRST_MESSAGE_ID); assertNotNull (editFirstMessage); assertEquals (FIRST_MESSAGE_ID, bewerktFirstMessage.getId ()); assertEquals (EDITTED_CONTENT, bewerktFirstMessage.getContent ()); }

Maar als een gebruiker met de ROLE_EDITOR role werkt de inhoud van het eerste bericht bij - ons systeem zal een AccessDeniedException:

@Test (verwacht = AccessDeniedException.class) @WithMockUser (rollen = {"EDITOR"}) openbare leegte gegevenRoleEditor_whenFind1stMessageByIdAndUpdateContent_thenFail () {NoticeMessage firstMessage = repo.findIDById (FIRSTID_MESS); assertNotNull (firstMessage); assertEquals (FIRST_MESSAGE_ID, firstMessage.getId ()); firstMessage.setContent (EDITTED_CONTENT); repo.save (firstMessage); }

Evenzo is het u gebruiker kan het tweede bericht vinden op ID, maar zal het niet updaten:

@Test @WithMockUser (gebruikersnaam = "hr") openbare ongeldige gegevenUsernameHr_whenFindMessageById2_thenOK () {NoticeMessage secondMessage = repo.findById (SECOND_MESSAGE_ID); assertNotNull (secondMessage); assertEquals (SECOND_MESSAGE_ID, secondMessage.getId ()); } @Test (verwacht = AccessDeniedException.class) @WithMockUser (gebruikersnaam = "hr") openbare ongeldige gegevenUsernameHr_whenUpdateMessageWithId2_thenFail () {NoticeMessage secondMessage = nieuwe NoticeMessage (); secondMessage.setId (SECOND_MESSAGE_ID); secondMessage.setContent (EDITTED_CONTENT); repo.save (secondMessage); }

5. Conclusie

We hebben de basisconfiguratie en het gebruik van Lente ACL in dit artikel.

Zoals we weten, Lente ACL vereiste specifieke tabellen voor het beheren van objecten, principes / bevoegdheden en machtigingen. Alle interacties met die tabellen, vooral bijwerkacties, moeten doorlopen AclService. We zullen deze service verkennen voor basis CRUD acties in een toekomstig artikel.

Standaard zijn we beperkt tot vooraf gedefinieerde toestemming in BasePermission klas.

Ten slotte is de implementatie van deze tutorial te vinden op Github.