[JAVA] Spring Security Usage Memo Domain Object Security (ACL)

Histoire de base et systématique Histoire de certification / autorisation Histoire Remember-Me Histoire CSRF Histoire de gestion de session L'histoire de l'en-tête de la réponse Histoire de la sécurité de la méthode Histoire CORS L'histoire de Run-As Test story Parlez de la coopération avec MVC et Boot

Édition supplémentaire Ce que Spring Security peut et ne peut pas faire

Qu'est-ce que la sécurité des objets de domaine?

Le traitement des autorisations en utilisant hasAuthority () etc. est essentiellement contrôlé en unités de fonctions. (Une certaine fonction (écran) doit avoir l'autorité de XX, etc.)

Cependant, lors de la création effective d'un système, il est rarement nécessaire de gérer les autorisations données par données. Par exemple, seules les personnes appartenant au même groupe que la personne qui a créé les données peuvent voir les données, ou seul le créateur ou l'administrateur système peut mettre à jour les données.

Spring Security fournit un mécanisme pour réaliser un tel contrôle d'accès sur une base de données par données.

Il est appelé sécurité des objets de domaine ou ACL (liste de contrôle d'accès).

Hello World

la mise en oeuvre

Dépendances

build.gradle


dependencies {
    compile 'org.springframework.security:spring-security-web:4.2.1.RELEASE'
    compile 'org.springframework.security:spring-security-config:4.2.1.RELEASE'
    compile 'org.springframework.security:spring-security-acl:4.2.1.RELEASE'★ Ajout
    compile 'org.springframework:spring-jdbc:4.3.7.RELEASE'
    compile 'com.h2database:h2:1.4.193'
}

DB

src/main/resources/sql/create_acl_tables.sql


CREATE TABLE ACL_SID (
    ID BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    PRINCIPAL BOOLEAN NOT NULL,
    SID VARCHAR_IGNORECASE(100) NOT NULL,
    CONSTRAINT UNIQUE_UK_1 UNIQUE(SID,PRINCIPAL)
);

CREATE TABLE ACL_CLASS(
    ID BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    CLASS VARCHAR_IGNORECASE(100) NOT NULL,
    CONSTRAINT UNIQUE_UK_2 UNIQUE(CLASS)
);

CREATE TABLE ACL_OBJECT_IDENTITY(
    ID BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    OBJECT_ID_CLASS BIGINT NOT NULL,
    OBJECT_ID_IDENTITY BIGINT NOT NULL,
    PARENT_OBJECT BIGINT,
    OWNER_SID BIGINT,
    ENTRIES_INHERITING BOOLEAN NOT NULL,
    CONSTRAINT UNIQUE_UK_3 UNIQUE(OBJECT_ID_CLASS, OBJECT_ID_IDENTITY),
    CONSTRAINT FOREIGN_FK_1 FOREIGN KEY(PARENT_OBJECT)REFERENCES ACL_OBJECT_IDENTITY(ID),
    CONSTRAINT FOREIGN_FK_2 FOREIGN KEY(OBJECT_ID_CLASS)REFERENCES ACL_CLASS(ID),
    CONSTRAINT FOREIGN_FK_3 FOREIGN KEY(OWNER_SID)REFERENCES ACL_SID(ID)
);

CREATE TABLE ACL_ENTRY(
    ID BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    ACL_OBJECT_IDENTITY BIGINT NOT NULL,
    ACE_ORDER INT NOT NULL,
    SID BIGINT NOT NULL,
    MASK INTEGER NOT NULL,
    GRANTING BOOLEAN NOT NULL,
    AUDIT_SUCCESS BOOLEAN NOT NULL,
    AUDIT_FAILURE BOOLEAN NOT NULL,
    CONSTRAINT UNIQUE_UK_4 UNIQUE(ACL_OBJECT_IDENTITY, ACE_ORDER),
    CONSTRAINT FOREIGN_FK_4 FOREIGN KEY(ACL_OBJECT_IDENTITY) REFERENCES ACL_OBJECT_IDENTITY(ID),
    CONSTRAINT FOREIGN_FK_5 FOREIGN KEY(SID) REFERENCES ACL_SID(ID)
);

src/main/resources/sql/insert_acl_tables.sql


INSERT INTO ACL_CLASS (ID, CLASS)
VALUES (100, 'sample.spring.security.domain.Foo');

INSERT INTO ACL_SID (ID, PRINCIPAL, SID)
VALUES (9, true, 'hoge');
INSERT INTO ACL_SID (ID, PRINCIPAL, SID)
VALUES (99, false, 'SAMPLE_AUTHORITY');

INSERT INTO ACL_OBJECT_IDENTITY (ID, OBJECT_ID_CLASS, OBJECT_ID_IDENTITY, PARENT_OBJECT, OWNER_SID, ENTRIES_INHERITING)
VALUES (1000, 100, 44, NULL, 9, true);

INSERT INTO ACL_ENTRY (ID, ACL_OBJECT_IDENTITY, ACE_ORDER, SID, MASK, GRANTING, AUDIT_SUCCESS, AUDIT_FAILURE)
VALUES (10, 1000, 0, 99, 1, true, false, false);

Mise en œuvre commune pour la confirmation

Foo.java


package sample.spring.security.domain;

public class Foo {
    private final long id;

    public Foo(long id) {
        this.id = id;
    }

    public long getId() {
        return id;
    }

    @Override
    public String toString() {
        return "Foo{id=" + id + '}';
    }
}

MyAclSampleService.java


package sample.spring.security.service;

import org.springframework.security.access.prepost.PreAuthorize;
import sample.spring.security.domain.Foo;

public class MyAclSampleService {
    
    @PreAuthorize("hasPermission(#foo, read)")
    public void logic(Foo foo) {
        System.out.println("foo=" + foo);
    }
}

MyAclServlet.java


package sample.spring.security.servlet;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import sample.spring.security.domain.Foo;
import sample.spring.security.service.MyAclSampleService;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.stream.Collectors;

@WebServlet("/acl")
public class MyAclServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.printPrincipal();
        this.callServiceLogic(new Foo(44L), req);
        this.callServiceLogic(new Foo(45L), req);
    }

    private void printPrincipal() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        String name = auth.getName();
        System.out.println("name=" + name);
        System.out.println("authorities=" +
                auth.getAuthorities().stream()
                        .map(GrantedAuthority::getAuthority)
                        .collect(Collectors.joining(", "))
        );
    }
    
    private void callServiceLogic(Foo foo, HttpServletRequest req) {
        try {
            this.findServiceBean(req).logic(foo);
        } catch (AccessDeniedException e) {
            System.out.println("AccessDeniedException : " + e.getMessage());
        }
    }
    
    private MyAclSampleService findServiceBean(HttpServletRequest req) {
        WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(req.getServletContext());
        return context.getBean(MyAclSampleService.class);
    }
}

Réglage

namespace

applicationContext.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/security
         http://www.springframework.org/schema/security/spring-security.xsd
         http://www.springframework.org/schema/jdbc
         http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">

    <jdbc:embedded-database id="dataSource" type="H2">
        <jdbc:script location="classpath:/sql/create_acl_tables.sql" />
        <jdbc:script location="classpath:/sql/insert_acl_tables.sql" />
    </jdbc:embedded-database>
    
    <sec:global-method-security pre-post-annotations="enabled">
        <sec:expression-handler ref="expressionHandler" />
    </sec:global-method-security>
    
    <bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
        <property name="permissionEvaluator">
            <bean class="org.springframework.security.acls.AclPermissionEvaluator">
                <constructor-arg ref="aclService" />
            </bean>
        </property>
    </bean>

    <bean id="aclService" class="org.springframework.security.acls.jdbc.JdbcAclService">
        <constructor-arg ref="dataSource" />
        <constructor-arg ref="lookupStrategy" />
    </bean>

    <bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
        <constructor-arg ref="dataSource" />
        <constructor-arg ref="aclCache" />
        <constructor-arg ref="aclAuthorizationStrategy" />
        <constructor-arg ref="permissionGrantingStrategy" />
    </bean>

    <bean id="aclCache" class="org.springframework.security.acls.domain.SpringCacheBasedAclCache">
        <constructor-arg>
            <bean class="org.springframework.cache.support.NoOpCache">
                <constructor-arg value="myCache" />
            </bean>
        </constructor-arg>
        <constructor-arg ref="permissionGrantingStrategy" />
        <constructor-arg ref="aclAuthorizationStrategy" />
    </bean>

    <bean id="aclAuthorizationStrategy" class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
        <constructor-arg>
            <list>
                <bean class="org.springframework.security.core.authority.SimpleGrantedAuthority">
                    <constructor-arg value="TEST"/>
                </bean>
            </list>
        </constructor-arg>
    </bean>

    <bean id="permissionGrantingStrategy" class="org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy">
        <constructor-arg>
            <bean class="org.springframework.security.acls.domain.ConsoleAuditLogger" />
        </constructor-arg>
    </bean>
    
    <bean class="sample.spring.security.service.MyAclSampleService" />

    <sec:http>
        <sec:intercept-url pattern="/login" access="permitAll" />
        <sec:intercept-url pattern="/**" access="isAuthenticated()" />
        <sec:form-login />
        <sec:logout />
    </sec:http>

    <sec:authentication-manager>
        <sec:authentication-provider>
            <sec:user-service>
                <sec:user name="foo" password="foo" authorities="" />
                <sec:user name="bar" password="bar" authorities="SAMPLE_AUTHORITY" />
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>
</beans

Java Configuration

MySpringSecurityConfig.java


package sample.spring.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import sample.spring.security.service.MyAclSampleService;

import java.util.Collections;

@EnableWebSecurity
@Import(MyGlobalMethodSecurityConfig.class)
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin();
    }
    
    @Bean
    public MyAclSampleService myAclSampleService() {
        return new MyAclSampleService();
    }
    
    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("foo")
                .password("foo")
                .authorities(Collections.emptyList())
            .and()
                .withUser("bar")
                .password("bar")
                .authorities("SAMPLE_AUTHORITY");
    }
}

MyGlobalMethodSecurityConfig.java


package sample.spring.security;

import org.springframework.cache.support.NoOpCache;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.acls.AclPermissionEvaluator;
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
import org.springframework.security.acls.domain.ConsoleAuditLogger;
import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;
import org.springframework.security.acls.domain.SpringCacheBasedAclCache;
import org.springframework.security.acls.jdbc.BasicLookupStrategy;
import org.springframework.security.acls.jdbc.JdbcAclService;
import org.springframework.security.acls.jdbc.LookupStrategy;
import org.springframework.security.acls.model.AclCache;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import javax.sql.DataSource;

@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MyGlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
                .generateUniqueName(true)
                .setType(EmbeddedDatabaseType.H2)
                .setScriptEncoding("UTF-8")
                .addScripts("/sql/create_acl_tables.sql", "/sql/insert_acl_tables.sql")
                .build();
    }

    @Bean
    public PermissionEvaluator permissionEvaluator(AclService aclService) {
        return new AclPermissionEvaluator(aclService);
    }

    @Bean
    public AclService aclService(DataSource dataSource, LookupStrategy lookupStrategy) {
        return new JdbcAclService(dataSource, lookupStrategy);
    }

    @Bean
    public LookupStrategy lookupStrategy(DataSource dataSource, AclCache aclCache, AclAuthorizationStrategy aclAuthorizationStrategy, PermissionGrantingStrategy permissionGrantingStrategy) {
        return new BasicLookupStrategy(
                dataSource,
                aclCache,
                aclAuthorizationStrategy,
                permissionGrantingStrategy
        );
    }

    @Bean
    public AclCache aclCache(PermissionGrantingStrategy permissionGrantingStrategy, AclAuthorizationStrategy aclAuthorizationStrategy) {
        return new SpringCacheBasedAclCache(
                new NoOpCache("myCache"),
                permissionGrantingStrategy,
                aclAuthorizationStrategy
        );
    }
    
    @Bean
    public AclAuthorizationStrategy aclAuthorizationStrategy() {
        return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("TEST"));
    }

    @Bean
    public PermissionGrantingStrategy permissionGrantingStrategy() {
        return new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger());
    }
}

Contrôle de fonctionnement

Connectez-vous en tant qu'utilisateur foo et accédez à / acl.

Sortie de la console du serveur


name=foo
authorities=
AccessDeniedException : Access is denied
AccessDeniedException : Access is denied

L'accès a été refusé pour ʻid = 44 et ʻid = 45.

Puis connectez-vous en tant qu'utilisateur bar et accédez à / acl.

Sortie de la console du serveur


name=bar
authorities=SAMPLE_AUTHORITY
foo=Foo{id=44}
AccessDeniedException : Access is denied

ʻId = 44 a réussi à exécuter la méthode logic () et ʻid = 45 a refusé l'accès.

Comment ça fonctionne

table

Les quatre suivants apparaîtront.

La structure du tableau ressemble à celle ci-dessous.

ACLテーブル構造.png

La flèche représente FK, la source de la flèche est la table de référence et la pointe de la flèche est la table de référence.

La signification de chaque tableau et colonne est la suivante.

ACL_CLASS

colonne sens
CLASS Nom de classe Java (FQCN) de l'objet de domaine

ACL_SID

colonne sens
PRINCIPAL Un indicateur qui distingue si cet enregistrement est un principal
true→ Directeur
falseGrantedAuthority
SID Une chaîne qui représente le SID.
Si vous êtes un directeurusernameGrantedAuthorityEnsuite, l'expression de chaîne de caractères est définie

ACL_OBJECT_IDENTITY --Table qui contient chaque instance d'un objet de domaine

colonne sens
OBJECT_ID_CLASS Une colonne qui pointe vers une classe d'objets de domaine.
ACL_CLASSRéférence externe à.
OBJECT_ID_IDENTITY ID qui identifie l'instance
PARENT_OBJECT ParentsACL_OBJECT_IDENTITYID
OWNER_SID Créé cette instanceACL_SIDID
ENTRIES_INHERITING Indique si cette instance a une relation d'héritage de privilèges avec d'autres instances

ACL_ENTRY

colonne sens
ACL_OBJECT_IDENTITY Appliquer cette définition d'autorisationACL_OBJECT_IDENTITYID
ACE_ORDER UnACL_OBJECT_IDENTITYÀ plusieursACL_ENTRTYCommander quand est lié
SID Appliquer cette définition d'autorisationACL_SIDID
MASK Valeur entière représentant la définition de l'autorisation (les détails seront décrits plus tard)
GRANTING Indicateur "Accorder" ou "Rejeter"
AUDIT_SUCCESS Indicateur de sortie du journal d'audit lorsque cette définition d'autorisation est accordée
AUDIT_FAILUER Indicateur de sortie du journal d'audit lorsque cette définition d'autorisation est refusée

Données Hello World

Les données utilisées dans Hello World avaient une structure comme ↓.

ACL_HelloWorld_データ.png

--ʻACL_SID` Deux enregistrements sont enregistrés dans la table

Type d'identifiant d'objet de domaine

--Un nombre doit être stocké dans ʻOBJECT_ID_IDENTITYcomme identifiant de l'objet de domaine. --Correspond au typelong` en Java

We do not intend to support non-long identifiers in Spring Security’s ACL module, as longs are already compatible with all database sequences, the most common identifier data type, and are of sufficient length to accommodate all common usage scenarios.

(Traduction) Nous n'avons pas l'intention de ** prendre en charge les identifiants non «longs» dans le module Spring Security ACL **. long est déjà compatible avec toutes les séquences de base de données, est le type de données le plus courant et est suffisamment long pour stocker tous les scénarios d'utilisation courants.

27.3 Getting Started

Paramètres de Bean pour activer ACL

Extension SecurityExpressionHandler

applicationContext.xml


    <sec:global-method-security pre-post-annotations="enabled">
        <sec:expression-handler ref="expressionHandler" />
    </sec:global-method-security>
    
    <bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
        <property name="permissionEvaluator">
            <bean class="org.springframework.security.acls.AclPermissionEvaluator">
                <constructor-arg ref="aclService" />
            </bean>
        </property>
    </bean>

Définition de AclService

applicationContext.xml


    <bean id="aclService" class="org.springframework.security.acls.jdbc.JdbcAclService">
        <constructor-arg ref="dataSource" />
        <constructor-arg ref="lookupStrategy" />
    </bean>

--ʻAclService fournit la fonctionnalité ACL --JdbcAclService` utilise JDBC pour accéder aux informations définies dans la base de données et implémenter la fonctionnalité ACL.

LookupStrategy

applicationContext.xml


    <bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
        <constructor-arg ref="dataSource" />
        <constructor-arg ref="aclCache" />
        <constructor-arg ref="aclAuthorizationStrategy" />
        <constructor-arg ref="permissionGrantingStrategy" />
    </bean>

Comment fonctionne le cache

applicationContext.xml


    <bean id="aclCache" class="org.springframework.security.acls.domain.SpringCacheBasedAclCache">
        <constructor-arg>
            <bean class="org.springframework.cache.support.NoOpCache">
                <constructor-arg value="myCache" />
            </bean>
        </constructor-arg>
        <constructor-arg ref="permissionGrantingStrategy" />
        <constructor-arg ref="aclAuthorizationStrategy" />
    </bean>

AclAuthorizationStrategy et PermissionGrantingStrategy

--SpringCacheBasedAclCache met en cache les objets de la classe ʻAclImpl`

AclImpl.java


	private Acl parentAcl;
	private transient AclAuthorizationStrategy aclAuthorizationStrategy;
	private transient PermissionGrantingStrategy permissionGrantingStrategy;
	private final List<AccessControlEntry> aces = new ArrayList<AccessControlEntry>();
	private ObjectIdentity objectIdentity;
	private Serializable id;
	private Sid owner; // OwnershipAcl
	private List<Sid> loadedSids = null; // includes all SIDs the WHERE clause covered,

--ʻAclImpl implémente l'interface ʻAcl, et ʻAcl hérite de Serializable`.

Au fait, BasicLookupStrategy doit également recevoir des instances de ʻAclAuthorizationStrategy et de PermissionGrantingStrategy` comme arguments de constructeur.

applicationContext.xml


    <bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
        ...
        <constructor-arg ref="aclAuthorizationStrategy" />
        <constructor-arg ref="permissionGrantingStrategy" />
    </bean>

Ceci est utilisé pour définir lorsque BasicLookupStrategy reconstruit l'objet ʻAclImpl` à partir des informations de la base de données.

AclAuthorizationStrategy

applicationContext.xml


    <bean id="aclAuthorizationStrategy" class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
        <constructor-arg>
            <list>
                <bean class="org.springframework.security.core.authority.SimpleGrantedAuthority">
                    <constructor-arg value="TEST"/>
                </bean>
            </list>
        </constructor-arg>
    </bean>

--ʻAclAuthorizationStrategy` a le rôle de vérifier si le principal actuel a cette autorité lors de la modification de la définition d'une ACL. ――En d'autres termes, il n'est pas réellement utilisé dans ce Hello World

PermissionGrantingStrategy

applicationContext.xml


    <bean id="permissionGrantingStrategy" class="org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy">
        <constructor-arg>
            <bean class="org.springframework.security.acls.domain.ConsoleAuditLogger" />
        </constructor-arg>
    </bean>

--PermissionGrantingStrategy fournit un processus pour déterminer spécifiquement si le principal actuel a les autorisations spécifiées parhasPermission ().

AuditLogger --ʻAuditLogger fournit un processus pour enregistrer le résultat du jugement d'autorisation. --ConsoleAuditLogger` est la seule classe d'implémentation fournie et affiche le résultat sur la console.

Diagramme de classe résumant l'histoire jusqu'à présent

spring-security.png

Comme ça.

hasPermission()

MyAclSampleService.java


    @PreAuthorize("hasPermission(#foo, read)")
    public void logic(Foo foo) {

--Utilisez l'expression hasPermission () pour déterminer l'ACL --Spécifiez l'objet de domaine dans le premier argument et l'autorisation dans le deuxième argument

Comment obtenir un identifiant à partir d'un objet de domaine

Spécifiez par ID uniquement

MyAclSampleService.java


    @PreAuthorize("hasPermission(#id, 'sample.spring.security.domain.Foo', read)")
    public void logic(long id) {

Spécification des autorisations

--La constante read est spécifiée pour spécifier l'autorisation.

SecurityExpressionRoot.java


	public final String read = "read";
	public final String write = "write";
	public final String create = "create";
	public final String delete = "delete";
	public final String admin = "administration";

Définition des permissions

Les définitions des autorisations («lecture», «écriture», «création», «suppression», «administration») sont exprimées sous forme de valeurs entières. Sur la base de données, il est stocké dans la colonne MASK de ʻACL_ENTRY`.

Chaque autorisation est associée à chaque bit binaire comme indiqué ci-dessous.

5ème bit 4e bit 3e bit 2ème bit 1er bit
administration delete create write read

En d'autres termes

autorisation Représentation binaire Représentation décimale
read 00001 1
write 00010 2
create 00100 4
delete 01000 8
administration 10000 16

Vous pourriez vous demander: "Alors, si vous voulez avoir deux permissions," read "et" write ", est-ce" 00011 "(" 3 "en décimal)?", Mais ce n'est pas le cas. Au dernier, l'un des cinq ci-dessus est entré dans MASK de chaque enregistrement ʻACL_ENTRY`. Lors de l'octroi de plusieurs autorisations, le nombre d'enregistrements sera enregistré. (Est-ce un masque?)

Vérification

Des données d'entrée


INSERT INTO ACL_CLASS (ID, CLASS)
VALUES (100, 'sample.spring.security.domain.Foo');

INSERT INTO ACL_SID (ID, PRINCIPAL, SID)
VALUES (9, true, 'hoge');
INSERT INTO ACL_SID (ID, PRINCIPAL, SID)
VALUES (98, false, 'AUTHORITY_10101');
INSERT INTO ACL_SID (ID, PRINCIPAL, SID)
VALUES (99, false, 'AUTHORITY_01010');

INSERT INTO ACL_OBJECT_IDENTITY (ID, OBJECT_ID_CLASS, OBJECT_ID_IDENTITY, PARENT_OBJECT, OWNER_SID, ENTRIES_INHERITING)
VALUES (1000, 100, 44, NULL, 9, true);

-- AUTHORITY_10101
INSERT INTO ACL_ENTRY (ID, ACL_OBJECT_IDENTITY, ACE_ORDER, SID, MASK, GRANTING, AUDIT_SUCCESS, AUDIT_FAILURE)
VALUES (10, 1000, 0, 98, 1, true, false, false); -- read(00001 = 1)
INSERT INTO ACL_ENTRY (ID, ACL_OBJECT_IDENTITY, ACE_ORDER, SID, MASK, GRANTING, AUDIT_SUCCESS, AUDIT_FAILURE)
VALUES (11, 1000, 1, 98, 4, true, false, false); -- create(00100 = 4)
INSERT INTO ACL_ENTRY (ID, ACL_OBJECT_IDENTITY, ACE_ORDER, SID, MASK, GRANTING, AUDIT_SUCCESS, AUDIT_FAILURE)
VALUES (12, 1000, 2, 98, 16, true, false, false); -- administration(10000 = 16)

-- AUTHORITY_01010
INSERT INTO ACL_ENTRY (ID, ACL_OBJECT_IDENTITY, ACE_ORDER, SID, MASK, GRANTING, AUDIT_SUCCESS, AUDIT_FAILURE)
VALUES (13, 1000, 3, 99, 10, true, false, false); -- write, delete(01010 = 10) 

applicationContext.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       ...>

    ...

    <sec:authentication-manager>
        <sec:authentication-provider>
            <sec:user-service>
                <sec:user name="foo" password="foo" authorities="AUTHORITY_10101" />
                <sec:user name="bar" password="bar" authorities="AUTHORITY_01010" />
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>
</beans>

MyAclSampleService.java


package sample.spring.security.service;

import org.springframework.security.access.prepost.PreAuthorize;
import sample.spring.security.domain.Foo;

public class MyAclSampleService {
    
    @PreAuthorize("hasPermission(#foo, read)")
    public void read(Foo foo) {
        System.out.println("[read] foo=" + foo);
    }

    @PreAuthorize("hasPermission(#foo, write)")
    public void write(Foo foo) {
        System.out.println("[write] foo=" + foo);
    }

    @PreAuthorize("hasPermission(#foo, create)")
    public void create(Foo foo) {
        System.out.println("[create] foo=" + foo);
    }

    @PreAuthorize("hasPermission(#foo, delete)")
    public void delete(Foo foo) {
        System.out.println("[delete] foo=" + foo);
    }

    @PreAuthorize("hasPermission(#foo, admin)")
    public void admin(Foo foo) {
        System.out.println("[admin] foo=" + foo);
    }
}

--Spécifiez les autorisations requises avec hasPermission () pour chaque méthode

MyAclServlet.java


package sample.spring.security.servlet;

...

@WebServlet("/acl")
public class MyAclServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.printPrincipal();
        MyAclSampleService serviceBean = this.findServiceBean(req);
        Foo foo = new Foo(44L);
        this.callServiceLogic("read", () -> serviceBean.read(foo));
        this.callServiceLogic("write", () -> serviceBean.write(foo));
        this.callServiceLogic("create", () -> serviceBean.create(foo));
        this.callServiceLogic("delete", () -> serviceBean.delete(foo));
        this.callServiceLogic("admin", () -> serviceBean.admin(foo));
    }

    private void printPrincipal() {
        ...
    }
    
    private void callServiceLogic(String methodName, Runnable runnable) {
        try {
            System.out.println("* invoke " + methodName + "()");
            runnable.run();
        } catch (AccessDeniedException e) {
            System.out.println("AccessDeniedException : " + e.getMessage());
        }
    }
    
    private MyAclSampleService findServiceBean(HttpServletRequest req) {
        ...
    }
}

** Contrôle de fonctionnement **

Sortie du serveur lors de la connexion avec foo


name=foo
authorities=AUTHORITY_10101

* invoke read()
[read] foo=Foo{id=44}

* invoke write()
AccessDeniedException : Access is denied

* invoke create()
[create] foo=Foo{id=44}

* invoke delete()
AccessDeniedException : Access is denied

* invoke admin()
[admin] foo=Foo{id=44}

Sortie du serveur lors de la connexion avec bar


name=bar
authorities=AUTHORITY_01010

* invoke read()
AccessDeniedException : Access is denied

* invoke write()
AccessDeniedException : Access is denied

* invoke create()
AccessDeniedException : Access is denied

* invoke delete()
AccessDeniedException : Access is denied

* invoke admin()
AccessDeniedException : Access is denied

--write et delete ont également été bloqués

Pourquoi le masque n'est pas un peu masque

La raison a été décrite dans Ce problème sur GitHub.

Je suis sceptique que cela me convienne parce que ma capacité en anglais est faible, mais j'ai l'impression que c'est comme suit.

  1. Lorsque vous refusez l'autorisation d'un parent avec l'autorisation d'un enfant lors de l'utilisation de l'héritage d'autorisation, il semble difficile de déterminer quelle autorisation est refusée avec un masque de bits.
  2. Lors de la recherche avec SQL, le masque de bits rend la recherche difficile.

Donc, même s'il dit «MASK», cela ressemble en fait à une division de valeurs entières.

Après tout, "J'ai préparé un point d'extension, donc si vous voulez vraiment juger avec un peu de masque, veuillez l'utiliser." SEC-1166: Provide strategy interface for AclImpl isGranted() method.

ACL CRUD

Jusqu'à ce point, des données telles que ʻACL_ENTRY` étaient enregistrées en préparant une instruction INSERT dans SQL à l'avance.

Cependant, il est difficile d'éditer ces tableaux directement dans le développement réel car vous devez comprendre correctement les spécifications.

Par conséquent, il existe une API pour maintenir ces données.

Enregistrer une nouvelle instance ACL

la mise en oeuvre

MyAclSampleService.java


package sample.spring.security.service;

import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.MutableAclService;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.transaction.annotation.Transactional;
import sample.spring.security.domain.Foo;

@Transactional
public class MyAclSampleService {
    
    private MutableAclService aclService;

    public MyAclSampleService(MutableAclService aclService) {
        this.aclService = aclService;
    }

    public void createObjectIdentity() {
        ObjectIdentity objectIdentity = new ObjectIdentityImpl(Foo.class, 10L);
        MutableAcl acl = this.aclService.createAcl(objectIdentity);
        System.out.println("acl = " + acl);
    }
}

MyAclServlet.java


package sample.spring.security.servlet;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import sample.spring.security.service.MyAclSampleService;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;

@WebServlet("/acl")
public class MyAclServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        MyAclSampleService service = this.findServiceBean(req, MyAclSampleService.class);
        service.createObjectIdentity();
        this.printTables(req);
    }
    
    private void printTables(HttpServletRequest req) {
        this.printTable(req, "ACL_SID");
        this.printTable(req, "ACL_CLASS");
        this.printTable(req, "ACL_OBJECT_IDENTITY");
        this.printTable(req, "ACL_ENTRY");
    }

    private void printTable(HttpServletRequest req, String table) {
        JdbcTemplate jdbcTemplate = this.findServiceBean(req, JdbcTemplate.class);
        List<Map<String, Object>> records = jdbcTemplate.queryForList("select * from " + table + " order by id asc");
        System.out.println("\n[" + table + "]");
        records.forEach(System.out::println);
    }

    private <T> T findServiceBean(HttpServletRequest req, Class<T> clazz) {
        WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(req.getServletContext());
        return context.getBean(clazz);
    }
}

namespace

applicationContext.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/security
         http://www.springframework.org/schema/security/spring-security.xsd
         http://www.springframework.org/schema/jdbc
         http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
         http://www.springframework.org/schema/tx
         http://www.springframework.org/schema/tx/spring-tx.xsd">

    <tx:annotation-driven />
    
    <jdbc:embedded-database id="dataSource" type="H2">
        <jdbc:script location="classpath:/sql/create_acl_tables.sql" />
    </jdbc:embedded-database>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource" />
    </bean>
    
    ...

    <bean id="aclService" class="org.springframework.security.acls.jdbc.JdbcMutableAclService">
        <constructor-arg ref="dataSource" />
        <constructor-arg ref="lookupStrategy" />
        <constructor-arg ref="aclCache" />
    </bean>

    ...
    
    <bean class="sample.spring.security.service.MyAclSampleService">
        <constructor-arg ref="aclService" />
    </bean>

    <bean class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg ref="dataSource" />
    </bean>

    ...
</beans>

Java Configuration

MySpringSecurityConfig.java


package sample.spring.security;

...
import org.springframework.security.acls.model.MutableAclService;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import sample.spring.security.service.MyAclSampleService;

...

@EnableWebSecurity
@EnableTransactionManagement
@Import(MyGlobalMethodSecurityConfig.class)
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        ...
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
    
    @Bean
    public MyAclSampleService myAclSampleService(MutableAclService aclService) {
        return new MyAclSampleService(aclService);
    }
    
    ...
}

MyGlobalMethodSecurityConfig.java


package sample.spring.security;

...
import org.springframework.security.acls.jdbc.JdbcMutableAclService;
...


@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MyGlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    ...
    

    @Bean
    public AclService aclService(DataSource dataSource, LookupStrategy lookupStrategy, AclCache aclCache) {
        return new JdbcMutableAclService(dataSource, lookupStrategy, aclCache);
    }

    ...
}

Contrôle de fonctionnement

Accès pour exécuter MyAclServlet.

Sortie de la console du serveur


acl = AclImpl[
  id: 1;
  objectIdentity: org.springframework.security.acls.domain.ObjectIdentityImpl[
    Type: sample.spring.security.domain.Foo;
    Identifier: 10
  ];
  owner: PrincipalSid[
    foo
  ];
  no ACEs;
  inheriting: true;
  parent: Null;
  aclAuthorizationStrategy: org.springframework.security.acls.domain.AclAuthorizationStrategyImpl@2c7d9da4;
  permissionGrantingStrategy: org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy@634b81ac
]

[ACL_SID]
{ID=1, PRINCIPAL=true, SID=foo}

[ACL_CLASS]
{ID=1, CLASS=sample.spring.security.domain.Foo}

[ACL_OBJECT_IDENTITY]
{ID=1, OBJECT_ID_CLASS=1, OBJECT_ID_IDENTITY=10, PARENT_OBJECT=null, OWNER_SID=1, ENTRIES_INHERITING=true}

[ACL_ENTRY]

* La sortie d'Acl est en fait sortie sur une seule ligne, mais des sauts de ligne sont ajoutés pour faciliter la visualisation </ font>

La description

Activation de transaction

  • Les transactions déclaratives basées sur les annotations sont activées car vous devez contrôler la transaction lors de la mise à jour de la table ACL.

MyAclSampleService.java


package sample.spring.security.service;

...
import org.springframework.transaction.annotation.Transactional;
...

@Transactional
public class MyAclSampleService {
...

--Annoter la classe avec @ Transactional

namespace

applicationContext.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans ...
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
         ...
         http://www.springframework.org/schema/tx
         http://www.springframework.org/schema/tx/spring-tx.xsd">

    <tx:annotation-driven />
    
    ...

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource" />
    </bean>

    ...

--Activez les transactions basées sur les annotations avec <annotation-driven>

  • Enregistrez l'implémentation de PlatformTransactionManager ( DataSourceTransactionManager) avec le nom transactionManager.

Java Configuration

MySpringSecurityConfig.java


...
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
...
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

...

@EnableTransactionManagement
...
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    ...

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
  • Activer la gestion des transactions avec @ EnableTransactionManagement
  • Enregistrez DataSourceTransactionManager, qui est une implémentation de PlatformTransactionManager, en tant que bean.

Définition de JdbcMutableAclService

namespace

applicationContext.xml


    <bean id="aclService" class="org.springframework.security.acls.jdbc.JdbcMutableAclService">
        <constructor-arg ref="dataSource" />
        <constructor-arg ref="lookupStrategy" />
        <constructor-arg ref="aclCache" />
    </bean>

Java Configuration

MyGlobalMethodSecurityConfig.java


import org.springframework.security.acls.jdbc.JdbcMutableAclService;

...

    @Bean
    public AclService aclService(DataSource dataSource, LookupStrategy lookupStrategy, AclCache aclCache) {
        return new JdbcMutableAclService(dataSource, lookupStrategy, aclCache);
    }

--Il existe une classe appelée JdbcMutableAclService avec une méthode de mise à jour des ACL en tant que sous-classe de JdbcAclService. --Enregistrez un objet de cette classe en tant que bean au lieu de JdbcAclService

  • Vous transmettez ʻAclCache dans l'argument constructeur, ce qui est nécessaire car JdbcMutableAclService` supprime également ces informations du cache lorsque vous effectuez le processus de suppression de l'ACL.

Enregistrement d'ObjectIdentity

MyAclSampleService.java


package sample.spring.security.service;

import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.MutableAclService;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.transaction.annotation.Transactional;
import sample.spring.security.domain.Foo;

@Transactional
public class MyAclSampleService {
    
    private MutableAclService aclService;

    public MyAclSampleService(MutableAclService aclService) {
        this.aclService = aclService;
    }

    public void createObjectIdentity() {
        ObjectIdentity objectIdentity = new ObjectIdentityImpl(Foo.class, 10L);
        MutableAcl acl = this.aclService.createAcl(objectIdentity);
        System.out.println("acl = " + acl);
    }
}

--Créer un objet de ʻObjectIdentity (ʻObjectIdentityImpl) --ʻLe constructeur d'ObjectIdeneityImpla le premier objetClass` de l'objet de domaine,

  • Le second est l'identifiant de l'objet de domaine (ʻID) --Passez le ʻObjectIdentity créé à la méthodecreateAcl (ObjectIdentity)de MutableAclService
  • Les informations sont enregistrées dans ʻACL_OBJECT_IDENTITYlorsquecreateAcl () est exécuté. --ʻACL_CLASS est également créé si l'information n'existe pas --Retourné MutableAcl
Propriétaire

Sortie de la console du serveur


[ACL_SID]
{ID=1, PRINCIPAL=true, SID=foo}

[ACL_CLASS]
{ID=1, CLASS=sample.spring.security.domain.Foo}

[ACL_OBJECT_IDENTITY]
{ID=1, OBJECT_ID_CLASS=1, OBJECT_ID_IDENTITY=10, PARENT_OBJECT=null, OWNER_SID=1, ENTRIES_INHERITING=true}
{ID=2, OBJECT_ID_CLASS=1, OBJECT_ID_IDENTITY=11, PARENT_OBJECT=null, OWNER_SID=1, ENTRIES_INHERITING=true}

[ACL_ENTRY]

--Si vous regardez attentivement, le ʻOWNER_SID du ʻACL_OBJECT_IDENTITY créé est mis à ʻACL_SID of SID = foo`.

  • Ceci est enregistré dans les informations de l'utilisateur connecté (principal) lorsque createAcl () de MutableAclService est exécuté.

Classe Java ACL

Les informations ACL sont modélisées dans la structure de classe ci-dessous.

ACLのクラス図.png

  • ObjectIdentity --Un objet qui représente l'identifiant d'un objet de domaine
  • Vous pouvez faire référence à l'identificateur d'objet de domaine et au nom de classe.
  • Acl --L'objet principal de l'ACL --Inclut toutes les autorisations associées à ʻObjectIdentity`
  • AccessControlEntry --Un objet qui représente les autorisations individuelles attribuées à Sid pour ʻAcl`
  • Permission --Un objet avec des informations d'autorisation spécifiques
  • Sid --Un objet qui représente la cible à laquelle les autorisations sont attribuées
  • Soit Principal ou "Autorité accordée"

Lorsque createAcl () de MutableAclService est exécuté, ʻAcl, ʻAccessControlEntry, Permission et Sid sont construits sous la forme associée à ʻObjectIdentity` spécifiée par l'argument.

Rechercher des ACL existantes

la mise en oeuvre

MyAclSampleService.java


package sample.spring.security.service;

import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.model.Acl;

...
public class MyAclSampleService {
    
    private MutableAclService aclService;

    ...

    public void createObjectIdentity() {
        ...
    }
    
    public void findAcl() {
        ObjectIdentity objectIdentity = new ObjectIdentityImpl(Foo.class, 10L);
        Acl acl = aclService.readAclById(objectIdentity);
        System.out.println("acl = " + acl);
    }
}

MyAclServlet.java


package sample.spring.security.servlet;

...

@WebServlet("/acl")
public class MyAclServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        MyAclSampleService service = this.findServiceBean(req, MyAclSampleService.class);
        service.createObjectIdentity();
        this.printTables(req);

        service.findAcl();
    }
    
    ...
}

Contrôle de fonctionnement

Sortie de la console du serveur (createObjectIdeneity)()La sortie est omise)


acl = AclImpl[
  id: 1;
  objectIdentity: org.springframework.security.acls.domain.ObjectIdentityImpl[
    Type: sample.spring.security.domain.Foo;
    Identifier: 10
  ];
  owner: PrincipalSid[
    foo
  ];
  no ACEs;
  inheriting: true;
  parent: Null;
  aclAuthorizationStrategy: org.springframework.security.acls.domain.AclAuthorizationStrategyImpl@2c7d9da4;
  permissionGrantingStrategy: org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy@634b81ac
]

La description

MyAclSampleService.java


    public void findAcl() {
        ObjectIdentity objectIdentity = new ObjectIdentityImpl(Foo.class, 10L);
        Acl acl = aclService.readAclById(objectIdentity);
        System.out.println("acl = " + acl);
    }

--ʻAclService fournit une méthode pour rechercher ʻAcl

  • Vous pouvez rechercher ʻAcl associé à ʻObjectIdentity avecreadAclById (ObjectIdentity).

Ajouter des autorisations

la mise en oeuvre

MyAclSampleService.java


package sample.spring.security.service;

import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.MutableAclService;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.transaction.annotation.Transactional;
import sample.spring.security.domain.Foo;

import java.util.List;

@Transactional
public class MyAclSampleService {
    
    private MutableAclService aclService;

    ...

    public void createObjectIdentity() {
        ...
    }
    
    public void findAcl() {
        ...
    }
    
    public void addPermission() {
        //Recherchez l'ACL à mettre à jour et
        ObjectIdentity objectIdentity = new ObjectIdentityImpl(Foo.class, 10L);
        MutableAcl acl = (MutableAcl) aclService.readAclById(objectIdentity);

        //Autorité"HOGE_AUTHORITY"Accordez les autorisations CREATE à
        List<AccessControlEntry> entries = acl.getEntries();
        GrantedAuthoritySid grantedAuthoritySid = new GrantedAuthoritySid(new SimpleGrantedAuthority("HOGE_AUTHORITY"));
        acl.insertAce(entries.size(), BasePermission.CREATE, grantedAuthoritySid, true);

        //Principal"test_user"Accorder l'autorisation WRITE à
        PrincipalSid principalSid = new PrincipalSid("test_user");
        acl.insertAce(entries.size(), BasePermission.WRITE, principalSid, true);

        //Enregistrer les modifications de l'ACL
        this.aclService.updateAcl(acl);
        
        System.out.println("acl = " + acl);
    }
}

MyAclServlet.java


package sample.spring.security.servlet;

...

@WebServlet("/acl")
public class MyAclServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        MyAclSampleService service = this.findServiceBean(req, MyAclSampleService.class);
        service.createObjectIdentity();
        this.printTables(req);
        
        service.findAcl();
        
        service.addPermission();
        this.printTables(req);
    }
    
    ...
}

Contrôle de fonctionnement

Sortie de la console du serveur


★ Avant d'ajouter des autorisations
acl = AclImpl[
  id: 1;
  objectIdentity: org.springframework.security.acls.domain.ObjectIdentityImpl[
    Type: sample.spring.security.domain.Foo;
    Identifier: 10
  ];
  owner: PrincipalSid[
    foo
  ];
  no ACEs;
  inheriting: true;
  parent: Null;
  aclAuthorizationStrategy: org.springframework.security.acls.domain.AclAuthorizationStrategyImpl@162ff71c;
  permissionGrantingStrategy: org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy@742976d3
]

[ACL_SID]
{ID=1, PRINCIPAL=true, SID=foo}

[ACL_CLASS]
{ID=1, CLASS=sample.spring.security.domain.Foo}

[ACL_OBJECT_IDENTITY]
{ID=1, OBJECT_ID_CLASS=1, OBJECT_ID_IDENTITY=10, PARENT_OBJECT=null, OWNER_SID=1, ENTRIES_INHERITING=true}

[ACL_ENTRY]

...

★ Après avoir ajouté des autorisations
acl = AclImpl[
  id: 1;
  objectIdentity: org.springframework.security.acls.domain.ObjectIdentityImpl[
    Type: sample.spring.security.domain.Foo;
    Identifier: 10
  ];
  owner: PrincipalSid[
    foo
  ];
  AccessControlEntryImpl[
    id: null;
    granting: true;
    sid: PrincipalSid[
      test_user
    ];
    permission: BasePermission[
      ..............................W.=2
    ];
    auditSuccess: false;
    auditFailure: false
  ]
  AccessControlEntryImpl[
    id: null;
    granting: true;
    sid: GrantedAuthoritySid[
      HOGE_AUTHORITY
    ];
    permission: BasePermission[
      .............................C..=4
    ];
    auditSuccess: false;
    auditFailure: false
  ]
  inheriting: true;
  parent: Null;
  aclAuthorizationStrategy: org.springframework.security.acls.domain.AclAuthorizationStrategyImpl@162ff71c;
  permissionGrantingStrategy: org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy@742976d3
]

[ACL_SID]
{ID=1, PRINCIPAL=true, SID=foo}
{ID=2, PRINCIPAL=true, SID=test_user}
{ID=3, PRINCIPAL=false, SID=HOGE_AUTHORITY}

[ACL_CLASS]
{ID=1, CLASS=sample.spring.security.domain.Foo}

[ACL_OBJECT_IDENTITY]
{ID=1, OBJECT_ID_CLASS=1, OBJECT_ID_IDENTITY=10, PARENT_OBJECT=null, OWNER_SID=1, ENTRIES_INHERITING=true}

[ACL_ENTRY]
{ID=1, ACL_OBJECT_IDENTITY=1, ACE_ORDER=0, SID=2, MASK=2, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}
{ID=2, ACL_OBJECT_IDENTITY=1, ACE_ORDER=1, SID=3, MASK=4, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}

La description

MyAclSampleService.java


    public void addPermission() {
        //Recherchez l'ACL à mettre à jour et
        ObjectIdentity objectIdentity = new ObjectIdentityImpl(Foo.class, 10L);
        MutableAcl acl = (MutableAcl) aclService.readAclById(objectIdentity);

        //Autorité"HOGE_AUTHORITY"Accordez les autorisations CREATE à
        List<AccessControlEntry> entries = acl.getEntries();
        GrantedAuthoritySid grantedAuthoritySid = new GrantedAuthoritySid(new SimpleGrantedAuthority("HOGE_AUTHORITY"));
        acl.insertAce(entries.size(), BasePermission.CREATE, grantedAuthoritySid, true);

        //Principal"test_user"Accorder l'autorisation WRITE à
        PrincipalSid principalSid = new PrincipalSid("test_user");
        acl.insertAce(entries.size(), BasePermission.WRITE, principalSid, true);

        //Enregistrer les modifications de l'ACL
        this.aclService.updateAcl(acl);
        
        System.out.println("acl = " + acl);
    }
  • Les autorisations peuvent être ajoutées à ʻAclen exécutant ʻinsertAce (int, Permission, Sid, boolean)de MutableAcl.
  • En interne, ʻAccessControlEntry est ajouté à ʻAcl.
  • Le premier «int» spécifie la position d'insertion de l'autorisation par l'index commençant par «0».
  • Les autorisations (ʻAccessControlEntry) sont conservées en interne dans ʻAcl as List et sont passées au premier argument de la méthode ʻadd (int, E) . --Donc, si vous passez une valeur inférieure à "0" ou supérieure à "size ()", une erreur se produira (si elle est identique à "size ()", elle sera ajoutée à la fin). --Dans DB, il devient ʻACE_ORDER de ʻACL_ENTRY --Permission peut spécifier les constantes définies dans la classe d'implémentation BasePermission --Sid spécifie GrantedAuthoritySidouPrincipalSid--Si vous souhaitez attribuer des autorisations aux autorisationsGrantedAuthoritySid--UtilisezPrincipalSid` pour attribuer des autorisations aux principaux
  • Le dernier booléen spécifie si la permission doit être" accordée "ou" refusée ".
  • "vrai" est accordé, "faux" est rejeté

Refuser les autorisations et ACE_ORDER

la mise en oeuvre

MyAclSampleService.java


package sample.spring.security.service;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.MutableAclService;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.transaction.annotation.Transactional;
import sample.spring.security.domain.Foo;

@Transactional
public class MyAclSampleService {
    
    private MutableAclService aclService;

    public MyAclSampleService(MutableAclService aclService) {
        this.aclService = aclService;
    }

    public void addPermission() {
        ObjectIdentity objectIdentity = new ObjectIdentityImpl(Foo.class, 10L);
        try {
            this.aclService.readAclById(objectIdentity);
        } catch (NotFoundException e) {
            MutableAcl acl = this.aclService.createAcl(objectIdentity);
            
            GrantedAuthoritySid deniedRead = new GrantedAuthoritySid(new SimpleGrantedAuthority("DENIED_READ"));
            acl.insertAce(0, BasePermission.READ, deniedRead, false);

            GrantedAuthoritySid permitRead = new GrantedAuthoritySid(new SimpleGrantedAuthority("PERMIT_READ"));
            acl.insertAce(1, BasePermission.READ, permitRead, true);
            
            this.aclService.updateAcl(acl);
        }
    }
    
    @PreAuthorize("hasPermission(#foo, read)")
    public void read(Foo foo) {
        System.out.println("read(" + foo + ")");
    }
}
  • La première fois, readAclById () ne peut pas obtenir l'ACL et NotFoundException est levée, alors enregistrez l'autorisation uniquement à ce moment-là. --Enregistrez l'autorité read avec" deny "dans l'autorité DENIED_READ, --Pour l'autorité PERMIT_READ, l'autorité read est enregistrée avec" Grant ".
  • De plus, l'ordre est de mettre DENIED_READ à 0 pour qu'il vienne en premier.
  • Ne rien faire si ACL est déjà enregistré
  • De plus, la méthode read () est annotée avec @ PreAuthorize, et hasPermission () vérifie si vous avez les privilèges read.
package sample.spring.security.servlet;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import sample.spring.security.domain.Foo;
import sample.spring.security.service.MyAclSampleService;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@WebServlet("/acl")
public class MyAclServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.printPrincipal();
        
        MyAclSampleService service = this.findServiceBean(req, MyAclSampleService.class);
        service.addPermission();

        this.printTables(req);
        
        try {
            service.read(new Foo(10L));
        } catch (AccessDeniedException e) {
            System.out.println(e.getMessage());
        }
    }
    
    ...
}
  • Sortir les informations du principal courant (printPrincipal ()) --Enregistrer ACL (ʻaddPermission () `)
  • Sortir les informations enregistrées dans la table (printTables ())
  • Exécutez la méthode read () et affichez un message si ʻAccessDeniedException` est lancé.

la mise en oeuvre

applicationContext.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       ...>

    ...

    <sec:authentication-manager>
        <sec:authentication-provider>
            <sec:user-service>
                <sec:user name="foo" password="foo" authorities="PERMIT_READ" />
                <sec:user name="bar" password="bar" authorities="PERMIT_READ,DENIED_READ" />
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>
</beans>

--Définissez deux utilisateurs, foo et bar --Définissez l'autorisation PERMIT_READ pour l'utilisateur foo --Définissez les autorisations PERMIT_READ et DENIED_READ sur l'utilisateur bar

Contrôle de fonctionnement

Connectez-vous en tant qu'utilisateur foo et accédez à / acl.

Sortie de la console du serveur


name=foo
authorities=PERMIT_READ

[ACL_SID]
{ID=1, PRINCIPAL=true, SID=foo}
{ID=2, PRINCIPAL=false, SID=DENIED_READ}
{ID=3, PRINCIPAL=false, SID=PERMIT_READ}

[ACL_CLASS]
{ID=1, CLASS=sample.spring.security.domain.Foo}

[ACL_OBJECT_IDENTITY]
{ID=1, OBJECT_ID_CLASS=1, OBJECT_ID_IDENTITY=10, PARENT_OBJECT=null, OWNER_SID=1, ENTRIES_INHERITING=true}

[ACL_ENTRY]
{ID=1, ACL_OBJECT_IDENTITY=1, ACE_ORDER=0, SID=2, MASK=1, GRANTING=false, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}
{ID=2, ACL_OBJECT_IDENTITY=1, ACE_ORDER=1, SID=3, MASK=1, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}

read(Foo{id=10})

La méthode read () peut être exécutée

Puis connectez-vous en tant qu'utilisateur bar et accédez à / acl.

Sortie de la console du serveur


name=bar
authorities=DENIED_READ, PERMIT_READ

[ACL_SID]
{ID=1, PRINCIPAL=true, SID=foo}
{ID=2, PRINCIPAL=false, SID=DENIED_READ}
{ID=3, PRINCIPAL=false, SID=PERMIT_READ}

[ACL_CLASS]
{ID=1, CLASS=sample.spring.security.domain.Foo}

[ACL_OBJECT_IDENTITY]
{ID=1, OBJECT_ID_CLASS=1, OBJECT_ID_IDENTITY=10, PARENT_OBJECT=null, OWNER_SID=1, ENTRIES_INHERITING=true}

[ACL_ENTRY]
{ID=1, ACL_OBJECT_IDENTITY=1, ACE_ORDER=0, SID=2, MASK=1, GRANTING=false, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}
{ID=2, ACL_OBJECT_IDENTITY=1, ACE_ORDER=1, SID=3, MASK=1, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}

Access is denied

L'accès a été refusé.

La description

MyAclSampleService.java


    MutableAcl acl = this.aclService.createAcl(objectIdentity);
    
    GrantedAuthoritySid deniedRead = new GrantedAuthoritySid(new SimpleGrantedAuthority("DENIED_READ"));
    acl.insertAce(0, BasePermission.READ, deniedRead, false);

    GrantedAuthoritySid permitRead = new GrantedAuthoritySid(new SimpleGrantedAuthority("PERMIT_READ"));
    acl.insertAce(1, BasePermission.READ, permitRead, true);
    
    this.aclService.updateAcl(acl);

applicationContext.xml


    <sec:user name="foo" password="foo" authorities="PERMIT_READ" />
    <sec:user name="bar" password="bar" authorities="PERMIT_READ,DENIED_READ" />

--ʻACE_ORDER` semble être appliqué avec la priorité donnée à la commande

  • L'utilisateur bar a également été défini sur PERMIT_READ, mais la permission a été déterminée comme étant" refusée "car" DENIED_READ précédait PERMIT_READ par ʻACE_ORDER.

Où l'utiliser?

HenLorsque j'ai vu ce mécanisme pour la première fois (refuser l'autorisation et la commande ʻACE`), je ne pouvais pas penser à une grande utilité.

  • Si vous voulez simplement refuser l'accès, vous n'êtes pas obligé de donner la permission, donc je ne savais pas quand donner la permission de "refuser". ―― La seule chose que j'ai trouvée était de savoir comment définir l'autorité "refuser totalement" comme ↑
  • Si la permission de type "accorder" est ajoutée à la fin et que la permission de type "refuser" est ajoutée au début, la permission de type "refuser" aura toujours la priorité.
  • Et si vous associez la permission "Refuser" à l'autorité (GrantedAuthority), cela se comportera comme" fondamentalement, vous ne pourrez pas accéder si vous avez cette permission ". ――Je ne sais pas s'il existe une telle situation, mais lorsque vous souhaitez essentiellement refuser l'accès, je pense que vous pouvez contrôler des choses comme "vous pouvez désactiver l'accès en définissant cette autorisation"

Vérifications des autorisations effectuées sur les mises à jour de l'ACL

Tout le monde ne peut pas mettre à jour l'ACL.

la mise en oeuvre

src/main/resources/sql/insert_acl_tables.sql


INSERT INTO ACL_CLASS (ID, CLASS)
VALUES (100, 'sample.spring.security.domain.Foo');

INSERT INTO ACL_SID (ID, PRINCIPAL, SID)
VALUES (9, true, 'hoge');
INSERT INTO ACL_SID (ID, PRINCIPAL, SID)
VALUES (19, true, 'admin');

INSERT INTO ACL_OBJECT_IDENTITY (ID, OBJECT_ID_CLASS, OBJECT_ID_IDENTITY, PARENT_OBJECT, OWNER_SID, ENTRIES_INHERITING)
VALUES (1000, 100, 10, NULL, 9, true);

INSERT INTO ACL_ENTRY (ID, ACL_OBJECT_IDENTITY, ACE_ORDER, SID, MASK, GRANTING, AUDIT_SUCCESS, AUDIT_FAILURE)
VALUES (10000, 1000, 0, 19, 16, true, false, false); --admin utilisateur admin(16)Donner la permission

--Créez ʻACL_OBJECT_IDENTITY pour l'objet Foo avec ʻID = 10 --Définir le principal hoge comme propriétaire

  • Accorder la permission «administration» au principal «administrateur»

applicationContext.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       ...>

    ...
    
    <jdbc:embedded-database id="dataSource" type="H2">
        <jdbc:script location="classpath:/sql/create_acl_tables.sql" />
        <jdbc:script location="classpath:/sql/insert_acl_tables.sql" />
    </jdbc:embedded-database>

    ...

    <bean id="aclAuthorizationStrategy" class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
        <constructor-arg>
            <list>
                <bean class="org.springframework.security.core.authority.SimpleGrantedAuthority">
                    <constructor-arg value="PERMISSION_MANAGER"/>
                </bean>
            </list>
        </constructor-arg>
    </bean>

    ...

    <sec:authentication-manager>
        <sec:authentication-provider>
            <sec:user-service>
                <sec:user name="hoge" password="hoge" authorities="" />
                <sec:user name="fuga" password="fuga" authorities="" />
                <sec:user name="piyo" password="piyo" authorities="PERMISSION_MANAGER" />
                <sec:user name="admin" password="admin" authorities="" />
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>
</beans>

--ʻAclAuthorizationStrategyImplpassePERMISSION_MANAGER` comme argument de constructeur

  • L'utilisateurhoge, fuga, ʻadmin` n'a pas de privilèges
  • Accorder des privilèges PERMISSION_MANAGER uniquement aux utilisateurs piyo

MyAclSampleService.java


package sample.spring.security.service;

import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.model.AlreadyExistsException;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.MutableAclService;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.transaction.annotation.Transactional;
import sample.spring.security.domain.Foo;

@Transactional
public class MyAclSampleService {
    
    private MutableAclService aclService;

    public MyAclSampleService(MutableAclService aclService) {
        this.aclService = aclService;
    }
    
    public void addPermission() {
        ObjectIdentity objectIdentity = new ObjectIdentityImpl(Foo.class, 10L);
        MutableAcl acl = (MutableAcl) this.aclService.readAclById(objectIdentity);
        
        acl.insertAce(
            acl.getEntries().size(),
            BasePermission.READ,
            new GrantedAuthoritySid(new SimpleGrantedAuthority("test")),
            true
        );
        
        this.aclService.updateAcl(acl);
    }
}
  • Recherche d'un objet Foo avec ʻid = 10 et ajout d'autorisations avec ʻinsertAce ()

MyAclServlet.java


package sample.spring.security.servlet;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import sample.spring.security.service.MyAclSampleService;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@WebServlet("/acl")
public class MyAclServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        MyAclSampleService service = this.findServiceBean(req, MyAclSampleService.class);

        this.printPrincipal();
        this.printTables(req);
        
        try {
            System.out.println("service.addPermission()");
            service.addPermission();
            this.printTables(req);
        } catch (AccessDeniedException | NotFoundException e) {
            System.out.println("e.class = " + e.getClass() + ", message = " + e.getMessage());
        }
    }
    
    ...
}
  • Sort les informations de l'utilisateur connecté et l'état de la base de données avant le traitement
  • Après avoir exécuté la méthode ʻaddPermission () de MyAclSampleService, affiche à nouveau l'état de la base de données. --Si une exception se produit dans ʻaddPermission () , cette information est sortie.

Contrôle de fonctionnement

** Lors de l'accès en tant qu'utilisateur hoge **

Sortie de la console du serveur


★ Informations utilisateur
name=hoge
authorities=

★ État du tableau avant mise à jour
[ACL_SID]
{ID=9, PRINCIPAL=true, SID=hoge}
{ID=19, PRINCIPAL=true, SID=admin}

[ACL_CLASS]
{ID=100, CLASS=sample.spring.security.domain.Foo}

[ACL_OBJECT_IDENTITY]
{ID=1000, OBJECT_ID_CLASS=100, OBJECT_ID_IDENTITY=10, PARENT_OBJECT=null, OWNER_SID=9, ENTRIES_INHERITING=true}

[ACL_ENTRY]
{ID=10000, ACL_OBJECT_IDENTITY=1000, ACE_ORDER=0, SID=19, MASK=16, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}

★ Exécution de la méthode
service.addPermission()

★ État du tableau après mise à jour
[ACL_SID]
{ID=9, PRINCIPAL=true, SID=hoge}
{ID=19, PRINCIPAL=true, SID=admin}
{ID=20, PRINCIPAL=false, SID=test}

[ACL_CLASS]
{ID=100, CLASS=sample.spring.security.domain.Foo}

[ACL_OBJECT_IDENTITY]
{ID=1000, OBJECT_ID_CLASS=100, OBJECT_ID_IDENTITY=10, PARENT_OBJECT=null, OWNER_SID=9, ENTRIES_INHERITING=true}

[ACL_ENTRY]
{ID=10001, ACL_OBJECT_IDENTITY=1000, ACE_ORDER=0, SID=19, MASK=16, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}
{ID=10002, ACL_OBJECT_IDENTITY=1000, ACE_ORDER=1, SID=20, MASK=1, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}
  • Les autorisations ont été ajoutées avec succès

** Lors de l'accès en tant qu'utilisateur fuga **

Sortie de la console du serveur


★ Informations utilisateur
name=fuga
authorities=

★ État du tableau avant mise à jour
[ACL_SID]
{ID=9, PRINCIPAL=true, SID=hoge}
{ID=19, PRINCIPAL=true, SID=admin}

[ACL_CLASS]
{ID=100, CLASS=sample.spring.security.domain.Foo}

[ACL_OBJECT_IDENTITY]
{ID=1000, OBJECT_ID_CLASS=100, OBJECT_ID_IDENTITY=10, PARENT_OBJECT=null, OWNER_SID=9, ENTRIES_INHERITING=true}

[ACL_ENTRY]
{ID=10000, ACL_OBJECT_IDENTITY=1000, ACE_ORDER=0, SID=19, MASK=16, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}

★ Exécution de la méthode
service.addPermission()

★ Informations d'erreur
e.class = class org.springframework.security.acls.model.NotFoundException, message = Unable to locate a matching ACE for passed permissions and SIDs
  • Les autorisations n'ont pas pu être ajoutées et une exception (NotFoundException) a été levée

** Lors de l'accès en tant qu'utilisateur piyo **

Sortie de la console du serveur


★ Informations utilisateur
name=piyo
authorities=PERMISSION_MANAGER

★ État du tableau avant mise à jour
[ACL_SID]
{ID=9, PRINCIPAL=true, SID=hoge}
{ID=19, PRINCIPAL=true, SID=admin}

[ACL_CLASS]
{ID=100, CLASS=sample.spring.security.domain.Foo}

[ACL_OBJECT_IDENTITY]
{ID=1000, OBJECT_ID_CLASS=100, OBJECT_ID_IDENTITY=10, PARENT_OBJECT=null, OWNER_SID=9, ENTRIES_INHERITING=true}

[ACL_ENTRY]
{ID=10000, ACL_OBJECT_IDENTITY=1000, ACE_ORDER=0, SID=19, MASK=16, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}

★ Exécution de la méthode
service.addPermission()

★ État du tableau après mise à jour
[ACL_SID]
{ID=9, PRINCIPAL=true, SID=hoge}
{ID=19, PRINCIPAL=true, SID=admin}
{ID=20, PRINCIPAL=false, SID=test}

[ACL_CLASS]
{ID=100, CLASS=sample.spring.security.domain.Foo}

[ACL_OBJECT_IDENTITY]
{ID=1000, OBJECT_ID_CLASS=100, OBJECT_ID_IDENTITY=10, PARENT_OBJECT=null, OWNER_SID=9, ENTRIES_INHERITING=true}

[ACL_ENTRY]
{ID=10001, ACL_OBJECT_IDENTITY=1000, ACE_ORDER=0, SID=19, MASK=16, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}
{ID=10002, ACL_OBJECT_IDENTITY=1000, ACE_ORDER=1, SID=20, MASK=1, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}
  • La mise à jour de l'autorisation a réussi

** Lors de l'accès en tant qu'utilisateur administrateur **

Sortie de la console du serveur


★ Informations utilisateur
name=admin
authorities=

★ État du tableau avant mise à jour
[ACL_SID]
{ID=9, PRINCIPAL=true, SID=hoge}
{ID=19, PRINCIPAL=true, SID=admin}

[ACL_CLASS]
{ID=100, CLASS=sample.spring.security.domain.Foo}

[ACL_OBJECT_IDENTITY]
{ID=1000, OBJECT_ID_CLASS=100, OBJECT_ID_IDENTITY=10, PARENT_OBJECT=null, OWNER_SID=9, ENTRIES_INHERITING=true}

[ACL_ENTRY]
{ID=10000, ACL_OBJECT_IDENTITY=1000, ACE_ORDER=0, SID=19, MASK=16, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}

★ Exécution de la méthode
service.addPermission()

★ État du tableau après mise à jour
[ACL_SID]
{ID=9, PRINCIPAL=true, SID=hoge}
{ID=19, PRINCIPAL=true, SID=admin}
{ID=20, PRINCIPAL=false, SID=test}

[ACL_CLASS]
{ID=100, CLASS=sample.spring.security.domain.Foo}

[ACL_OBJECT_IDENTITY]
{ID=1000, OBJECT_ID_CLASS=100, OBJECT_ID_IDENTITY=10, PARENT_OBJECT=null, OWNER_SID=9, ENTRIES_INHERITING=true}

[ACL_ENTRY]
{ID=10001, ACL_OBJECT_IDENTITY=1000, ACE_ORDER=0, SID=19, MASK=16, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}
{ID=10002, ACL_OBJECT_IDENTITY=1000, ACE_ORDER=1, SID=20, MASK=1, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}
  • La mise à jour de l'autorisation a réussi

La description

  • Les facteurs suivants interviennent dans la mise à jour des autorisations: --Si vous êtes le propriétaire de l'ACL que vous essayez de mettre à jour --Si l'ACL que vous essayez de mettre à jour a des autorisations «d'administration». ――Si vous avez l'autorité spécifiée dans l'argument constructeur de ʻAclAuthorizationStrategyImpl` --Type de mise à jour ACL (général, détenu, audité) --Et il est jugé s'il peut être mis à jour dans l'ordre suivant
  1. Pour le propriétaire de l'ACL que le principal actuel tente de mettre à jour, Autoriser </ font> si le type de mise à jour de l'ACL est "Général" ou "Possédé"
  2. Sinon, allow </ font> si vous disposez des autorisations spécifiées dans ʻAclAuthorizationStrategyImpl`
  3. Sinon, allow </ font> si l'ACL que vous essayez de mettre à jour dispose des autorisations «administration».
  4. Sinon non autorisé </ font>

Type de mise à jour

Il existe trois types de mises à jour d'ACL:

--Général

  • Possédé --Audit

Et les méthodes de mise à jour de l'ACL sont classées comme suit.

Méthode Type de mise à jour
insertAce() Général
updateAce() Général
deleteAce() Général
setParent() Général
setEntriesInheriting() Général
setOwner() Possédé
updateAuditing() Audit

Au fait, ces trois types sont définis dans l'interface ʻAclAuthorizationStrategy`.

AclAuthorizationStrategy.java


public interface AclAuthorizationStrategy {

	int CHANGE_OWNERSHIP = 0;Possédé
	int CHANGE_AUDITING = 1;Audit
	int CHANGE_GENERAL = 2;Général

Privilèges spécifiés dans AclAuthorizationStrategyImpl

applicationContext.xml


    <bean id="aclAuthorizationStrategy" class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
        <constructor-arg>
            <list>
                <bean class="org.springframework.security.core.authority.SimpleGrantedAuthority">
                    <constructor-arg value="PERMISSION_MANAGER"/>
                </bean>
            </list>
        </constructor-arg>
    </bean>

--ʻGrantedAuthority passé dans le constructeur de AclAuthorizationStrategyImpl est utilisé lors de la mise à jour de l'ACL.

  • Si l'utilisateur connecté qui essaie de mettre à jour dispose de l'autorité spécifiée ici, la mise à jour est autorisée.
Spécifiez les autorisations pour chaque type de mise à jour

Dans l'exemple ci-dessus, seule l'autorité «PERMISSION_MANAGER» est spécifiée. Dans ce cas, tous les types de mises à jour (générales, détenues, auditées) seront autorisés si vous disposez de cette autorité.

Si vous voulez contrôler "●● est requis pour la mise à jour" générale ", ▲▲ est requis pour la mise à jour" possédée "et ★★ est requis pour" audit "", comme suit. Définissez un bean dans.

applicationContext.xml


    <bean id="aclAuthorizationStrategy" class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
        <constructor-arg>
            <list>
                <bean class="org.springframework.security.core.authority.SimpleGrantedAuthority">
                    <constructor-arg value="PERMISSION_MANAGER_OWNERSHIP"/>
                </bean>
                <bean class="org.springframework.security.core.authority.SimpleGrantedAuthority">
                    <constructor-arg value="PERMISSION_MANAGER_AUDIT"/>
                </bean>
                <bean class="org.springframework.security.core.authority.SimpleGrantedAuthority">
                    <constructor-arg value="PERMISSION_MANAGER_GENERAL"/>
                </bean>
            </list>
        </constructor-arg>
    </bean>

--Définissez le nombre de GrantedAuthoritys passés à l'argument constructeur à 3 (notez que 2 ou 4 entraîneront une erreur)

  • Comme indiqué ci-dessous, la position du paramètre "GrantedAuthority" spécifié détermine le type de mise à jour auquel l'autorité est associée.
  1. Spécifiez les autorisations requises pour les mises à jour "détenues"
  2. Spécifiez les autorisations requises pour les mises à jour liées à «audit»
  3. Spécifiez les autorisations requises pour les mises à jour "générales"

À propos du comportement lorsque la mise à jour est refusée

Dans l'exemple ci-dessus, une NotFoundException a été lancée si la mise à jour a été refusée.

Ce n'est pas vrai que «NotFoundException» est toujours levé lorsqu'il n'est pas autorisé. Si l'ACL à mettre à jour a la permission de ʻadministration définie sur "deny", ʻAccessDeniedException est levée.

C'est en fait un ** bogue **, et dans les deux cas, il est correct de lancer ʻAccessDeniedException`.

Une recherche d'un problème sur GitHub révèle ce bogue a été soulevé. Cependant, depuis le 09 juillet 2017, ce problème reste OUVERT et ne semble pas être résolu. Il semble que ce problème ait été reconnu en 2009, il a donc été complètement négligé (sans s'y limiter, tous les problèmes liés à l'ACL ont été négligés pendant longtemps et peuvent ne pas être beaucoup entretenus. inconnue).

De plus, la vérification des autorisations pour la mise à jour de l'ACL ne fait pas référence au rôle parent lors de l'utilisation des hiérarchies de rôles [https://github.com/spring-projects/spring-security/issues] Des bogues tels que / 4186) ont été soulevés (également laissés sans surveillance pendant une longue période).

Si vous voulez utiliser cette zone de manière correcte, vous devez copier le ʻAclAuthorizationStrategyImpl existant, créer votre propre classe d'implémentation qui résout le problème, et l'utiliser à la place de ʻAclAuthorizationStrategyImpl. (Ou tirez-vous la demande de la version modifiée vers la famille principale)

Accorder aux autorisations une relation d'héritage

ʻACL_OBJECT_IDENTITY` peut avoir une relation d'héritage.

Hello World

la mise en oeuvre

insert_acl_tables.sql


INSERT INTO ACL_CLASS (ID, CLASS)
VALUES (100, 'sample.spring.security.domain.Foo');

INSERT INTO ACL_SID (ID, PRINCIPAL, SID)
VALUES (9, true, 'hoge');
INSERT INTO ACL_SID (ID, PRINCIPAL, SID)
VALUES (98, false, 'READONLY');

INSERT INTO ACL_OBJECT_IDENTITY (ID, OBJECT_ID_CLASS, OBJECT_ID_IDENTITY, PARENT_OBJECT, OWNER_SID, ENTRIES_INHERITING)
VALUES (1000, 100, 44, NULL, 9, false);
INSERT INTO ACL_OBJECT_IDENTITY (ID, OBJECT_ID_CLASS, OBJECT_ID_IDENTITY, PARENT_OBJECT, OWNER_SID, ENTRIES_INHERITING)
VALUES (1001, 100, 45, NULL, 9, false);
INSERT INTO ACL_OBJECT_IDENTITY (ID, OBJECT_ID_CLASS, OBJECT_ID_IDENTITY, PARENT_OBJECT, OWNER_SID, ENTRIES_INHERITING)
VALUES (1002, 100, 46, 1000, 9, false);
INSERT INTO ACL_OBJECT_IDENTITY (ID, OBJECT_ID_CLASS, OBJECT_ID_IDENTITY, PARENT_OBJECT, OWNER_SID, ENTRIES_INHERITING)
VALUES (1003, 100, 47, 1000, 9, true);

INSERT INTO ACL_ENTRY (ID, ACL_OBJECT_IDENTITY, ACE_ORDER, SID, MASK, GRANTING, AUDIT_SUCCESS, AUDIT_FAILURE)
VALUES (10, 1000, 0, 98, 1, true, false, false);

--Définir ʻACL_OBJECT_IDENTITY de ʻid = 44 ... 47 --Seulement ʻid = 44 a l'autorisation de lecture sur le privilège READONLY. --ʻId = 45 ne spécifie aucun objet parent, etc. --ʻId = 46spécifie uniquement l'ID de l'objet parent avecPARENT_OBJECT (ʻENTRIES_INHERITING est false) --ʻId = 47 définit ʻENTRIES_INHERITING sur true après avoir spécifié l'objet parent.

MyAclSampleService.java


package sample.spring.security.service;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import sample.spring.security.domain.Foo;

@Transactional
public class MyAclSampleService {

    @PreAuthorize("hasPermission(#foo, read)")
    public void read(Foo foo) {
        System.out.println("read(" + foo + ")");
    }
}
  • Vérifiez que vous disposez des autorisations de lecture sur l'objet «Foo» que vous recevez en argument

MyAclServlet.java


package sample.spring.security.servlet;

...

import sample.spring.security.domain.Foo;
import sample.spring.security.service.MyAclSampleService;

@WebServlet("/acl")
public class MyAclServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.printPrincipal();
        this.printTables(req);
        
        this.callServiceLogic(req, 44L);
        this.callServiceLogic(req, 45L);
        this.callServiceLogic(req, 46L);
        this.callServiceLogic(req, 47L);
    }

    private void callServiceLogic(HttpServletRequest req, long id) {
        try {
            System.out.println("id=" + id);
            MyAclSampleService service = this.findServiceBean(req);
            Foo foo = new Foo(id);
            service.read(foo);
        } catch (AccessDeniedException e) {
            System.out.println("AccessDeniedException : " + e.getMessage());
        }
    }
    
    ...
}

--Créez un objet Foo avec ʻid = 44 ... 47 et exécutez la méthode read () de MyAclSampleService`

applicationContext.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       ...>

    ...

    <sec:authentication-manager>
        <sec:authentication-provider>
            <sec:user-service>
                <sec:user name="foo" password="foo" authorities="READONLY" />
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>
</beans>
  • Accorder des privilèges READONLY à l'utilisateur foo

Contrôle de fonctionnement

Connectez-vous en tant qu'utilisateur foo et accédez à / acl.

Sortie de la console du serveur


name=foo
authorities=READONLY

[ACL_SID]
{ID=9, PRINCIPAL=true, SID=hoge}
{ID=98, PRINCIPAL=false, SID=READONLY}

[ACL_CLASS]
{ID=100, CLASS=sample.spring.security.domain.Foo}

[ACL_OBJECT_IDENTITY]
{ID=1000, OBJECT_ID_CLASS=100, OBJECT_ID_IDENTITY=44, PARENT_OBJECT=null, OWNER_SID=9, ENTRIES_INHERITING=false}
{ID=1001, OBJECT_ID_CLASS=100, OBJECT_ID_IDENTITY=45, PARENT_OBJECT=null, OWNER_SID=9, ENTRIES_INHERITING=false}
{ID=1002, OBJECT_ID_CLASS=100, OBJECT_ID_IDENTITY=46, PARENT_OBJECT=1000, OWNER_SID=9, ENTRIES_INHERITING=false}
{ID=1003, OBJECT_ID_CLASS=100, OBJECT_ID_IDENTITY=47, PARENT_OBJECT=1000, OWNER_SID=9, ENTRIES_INHERITING=true}

[ACL_ENTRY]
{ID=10, ACL_OBJECT_IDENTITY=1000, ACE_ORDER=0, SID=98, MASK=1, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}

id=44
read(Foo{id=44})
id=45
AccessDeniedException : Access is denied
id=46
AccessDeniedException : Access is denied
id=47
read(Foo{id=47})

--Seulement ʻid = 44, 47 peut exécuter la méthode, ʻid = 45, 46 est rejeté

La description

insert_acl_tables.sql


...

INSERT INTO ACL_OBJECT_IDENTITY (ID, OBJECT_ID_CLASS, OBJECT_ID_IDENTITY, PARENT_OBJECT, OWNER_SID, ENTRIES_INHERITING)
VALUES (1000, 100, 44, NULL, 9, false);
INSERT INTO ACL_OBJECT_IDENTITY (ID, OBJECT_ID_CLASS, OBJECT_ID_IDENTITY, PARENT_OBJECT, OWNER_SID, ENTRIES_INHERITING)
VALUES (1001, 100, 45, NULL, 9, false);
INSERT INTO ACL_OBJECT_IDENTITY (ID, OBJECT_ID_CLASS, OBJECT_ID_IDENTITY, PARENT_OBJECT, OWNER_SID, ENTRIES_INHERITING)
VALUES (1002, 100, 46, 1000, 9, false);
INSERT INTO ACL_OBJECT_IDENTITY (ID, OBJECT_ID_CLASS, OBJECT_ID_IDENTITY, PARENT_OBJECT, OWNER_SID, ENTRIES_INHERITING)
VALUES (1003, 100, 47, 1000, 9, true);

--Pour donner l'héritage des autorisations, ajoutez les deux paramètres suivants à ʻACL_OBJECT_IDENTITY --DéfinissezPARENT_OBJECT sur ʻID du parent ʻACL_OBJECT_IDENTITY --Régleztrue à ʻENTRIES_INHERITING

  • Ceci permet à l'autorisation d'être déterminée rétroactivement pour le parent lorsque l'autorisation est vérifiée pour l''ACL_OBJECT_IDENTITY` de l'enfant.

Utiliser comme autorisation par défaut

  • En utilisant l'héritage des permissions, vous pouvez réaliser des choses comme des "permissions par défaut" et gérer les informations de définition en un seul endroit.
  • ** C'est un mystère de savoir si son utilisation est correcte **, mais ʻOBJECT_IDENTITY de ʻACL_OBJECT_IDENTITY peut être enregistré même si ce n'est pas un ʻid existant. --Si vous enregistrez ʻACL_OBJECT_IDENTITY avec une valeur impossible telle que ʻOBJECT_IDENTITY = -1 et que d'autres ʻACL_OBJECT_IDENTITY normaux héritent de cette valeur par défaut ʻACL_OBJECT_IDENTITY, la permission par défaut sera réalisable ,Cependant, si l'héritage est superposé en plusieurs couches, je pense qu'il sera difficile de spécifier "toutes les autorisations définies dans cet ʻACL_OBJECT_IDENTITY" avec SQL seul (la valeur maximale de la couche est fixée). Si tel est le cas, est-ce que vous pouvez faire `` GAUCHE '' pour ce numéro?) «Je pense qu'il est plus sûr de garder la hiérarchie aussi petite que possible.

Parents programmés

la mise en oeuvre

MyAclSampleService.java


package sample.spring.security.service;

import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.MutableAclService;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.transaction.annotation.Transactional;
import sample.spring.security.domain.Foo;

@Transactional
public class MyAclSampleService {
    
    private MutableAclService aclService;

    public MyAclSampleService(MutableAclService aclService) {
        this.aclService = aclService;
    }

    public void init() {
        ObjectIdentityImpl parentId = new ObjectIdentityImpl(Foo.class, 44L);
        MutableAcl parentAcl = this.aclService.createAcl(parentId);
        parentAcl.insertAce(
            parentAcl.getEntries().size(),
            BasePermission.READ,
            new GrantedAuthoritySid(new SimpleGrantedAuthority("READONLY")),
            true
        );
        this.aclService.updateAcl(parentAcl);

        ObjectIdentityImpl childId = new ObjectIdentityImpl(Foo.class, 45L);
        MutableAcl childAcl = this.aclService.createAcl(childId);
        childAcl.setParent(parentAcl);
        this.aclService.updateAcl(childAcl);
    }
}

python


package sample.spring.security.servlet;

...

@WebServlet("/acl")
public class MyAclServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        MyAclSampleService service = this.findServiceBean(req);
        service.init();

        this.printTables(req);
    }
    
    ...
}

Contrôle de fonctionnement

Accéder à / acl

Sortie du journal du serveur


[ACL_SID]
{ID=1, PRINCIPAL=true, SID=foo}
{ID=2, PRINCIPAL=false, SID=READONLY}

[ACL_CLASS]
{ID=1, CLASS=sample.spring.security.domain.Foo}

[ACL_OBJECT_IDENTITY]
{ID=1, OBJECT_ID_CLASS=1, OBJECT_ID_IDENTITY=44, PARENT_OBJECT=null, OWNER_SID=1, ENTRIES_INHERITING=true}
{ID=2, OBJECT_ID_CLASS=1, OBJECT_ID_IDENTITY=45, PARENT_OBJECT=1, OWNER_SID=1, ENTRIES_INHERITING=true}

[ACL_ENTRY]
{ID=1, ACL_OBJECT_IDENTITY=1, ACE_ORDER=0, SID=2, MASK=1, GRANTING=true, AUDIT_SUCCESS=false, AUDIT_FAILURE=false}

La description

MyAclSampleService.java


    public void init() {
        ObjectIdentityImpl parentId = new ObjectIdentityImpl(Foo.class, 44L);
        MutableAcl parentAcl = this.aclService.createAcl(parentId);
        parentAcl.insertAce(
            parentAcl.getEntries().size(),
            BasePermission.READ,
            new GrantedAuthoritySid(new SimpleGrantedAuthority("READONLY")),
            true
        );
        this.aclService.updateAcl(parentAcl);

        ObjectIdentityImpl childId = new ObjectIdentityImpl(Foo.class, 45L);
        MutableAcl childAcl = this.aclService.createAcl(childId);
        childAcl.setParent(parentAcl); //★ Définissez les parents ici
        this.aclService.updateAcl(childAcl);
    }
  • Pour définir une ACL parent sur une ACL, utilisez la méthode setParent () de MutableAcl. --ʻENTRIES_INHERITINGprend la valeur par défauttrue quand une nouvelle ACL est créée, il n'est donc pas nécessaire de la spécifier lors de la création d'une nouvelle ACL (elle peut être définie avec la méthode setEntriesInheriting ()`)

Journal d'audit

Il existe un mécanisme pour afficher les informations sous forme de journal d'audit lorsque l'autorisation est accordée ou refusée.

Hello World

la mise en oeuvre

insert_acl_tables.sql


INSERT INTO ACL_CLASS (ID, CLASS)
VALUES (100, 'sample.spring.security.domain.Foo');

INSERT INTO ACL_SID (ID, PRINCIPAL, SID)
VALUES (9, true, 'hoge');
INSERT INTO ACL_SID (ID, PRINCIPAL, SID)
VALUES (98, false, 'READONLY');

INSERT INTO ACL_OBJECT_IDENTITY (ID, OBJECT_ID_CLASS, OBJECT_ID_IDENTITY, PARENT_OBJECT, OWNER_SID, ENTRIES_INHERITING)
VALUES (999, 100, 44, NULL, 9, false);

INSERT INTO ACL_ENTRY (ID, ACL_OBJECT_IDENTITY, ACE_ORDER, SID, MASK, GRANTING, AUDIT_SUCCESS, AUDIT_FAILURE)
VALUES (10, 999, 0, 98, 1, true, true, false);
INSERT INTO ACL_ENTRY (ID, ACL_OBJECT_IDENTITY, ACE_ORDER, SID, MASK, GRANTING, AUDIT_SUCCESS, AUDIT_FAILURE)
VALUES (11, 999, 1, 98, 2, false, false, true);

--Pour ʻid = 44deFoo, définissez les autorisations" accorder lecture" et "refuser écrire" pour le privilège READONLY. --ʻACL_ENTRY de "Grant read" change ʻAUDIT_SUCCESS en true --ʻACL_ENTRY dans "Reject write "définit ʻAUDIT_FAILURE sur true`

MyAclSampleService.java


package sample.spring.security.service;

import org.springframework.security.access.prepost.PreAuthorize;
import sample.spring.security.domain.Foo;

public class MyAclSampleService {

    @PreAuthorize("hasPermission(#foo, read)")
    public void read(Foo foo) {
        System.out.println(foo);
    }
    
    @PreAuthorize("hasPermission(#foo, write)")
    public void write(Foo foo) {
        System.out.println(foo);
    }
}

MyAclServlet.java


package sample.spring.security.servlet;

...

@WebServlet("/acl")
public class MyAclServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        MyAclSampleService service = this.findServiceBean(req);
        Foo foo = new Foo(44L);

        try {
            System.out.println("service.read()");
            service.read(foo);
            System.out.println("service.write()");
            service.write(foo);
        } catch (AccessDeniedException e) {
            System.out.println(e.getMessage());
        }
    }
    
    ...
}

applicationContext.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       ...>

    ...

    <bean id="permissionGrantingStrategy" class="org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy">
        <constructor-arg>
            <bean class="org.springframework.security.acls.domain.ConsoleAuditLogger" />
        </constructor-arg>
    </bean>

    ...

    <sec:authentication-manager>
        <sec:authentication-provider>
            <sec:user-service>
                <sec:user name="foo" password="foo" authorities="READONLY" />
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>
</beans>

Contrôle de fonctionnement

Connectez-vous en tant qu'utilisateur foo et accédez à / acl

Sortie de la console du serveur


service.read()
GRANTED due to ACE: AccessControlEntryImpl[id: 10; granting: true; sid: GrantedAuthoritySid[READONLY]; permission: BasePermission[...............................R=1]; auditSuccess: true; auditFailure: false]
Foo{id=44}

service.write()
DENIED due to ACE: AccessControlEntryImpl[id: 11; granting: false; sid: GrantedAuthoritySid[READONLY]; permission: BasePermission[..............................W.=2]; auditSuccess: false; auditFailure: true]
Access is denied
  • Avant d'exécuter chaque méthode, si l'autorisation a été accordée ou refusée est sortie sur la console.

La description

insert_acl_tables.sql


INSERT INTO ACL_ENTRY (ID, ACL_OBJECT_IDENTITY, ACE_ORDER, SID, MASK, GRANTING, AUDIT_SUCCESS, AUDIT_FAILURE)
VALUES (10, 999, 0, 98, 1, true, true, false);
INSERT INTO ACL_ENTRY (ID, ACL_OBJECT_IDENTITY, ACE_ORDER, SID, MASK, GRANTING, AUDIT_SUCCESS, AUDIT_FAILURE)
VALUES (11, 999, 1, 98, 2, false, false, true);
  • Pour activer la sortie du journal d'audit, définissez ʻAUDIT_SUCCESS ou ʻAUDIT_FAILURE de ʻACL_ENTRY sur true`.
  • Si ʻAUDIT_SUCCESS est ʻa vrai, un journal est produit lorsque l'octroi de l'autorisation est déterminé. --Si ʻAUDIT_FAILURE est ʻa vrai, un journal est produit lorsque le refus d'autorisation est déterminé.

applicationContext.xml


    <bean id="permissionGrantingStrategy" class="org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy">
        <constructor-arg>
            <bean class="org.springframework.security.acls.domain.ConsoleAuditLogger" />
        </constructor-arg>
    </bean>

--La sortie du journal d'audit est effectuée par une classe qui implémente l'interface ʻAuditLogger. --ʻAuditLogger est défini sur DefaultPermissionGrantingStrategy --ʻLa classe ConsoleAuditLogger, qui est fournie en standard comme implémentation d'AuditLogger`, envoie le journal à la sortie standard.

Implémentation de la sortie du journal d'audit par défaut

L'implémentation de ConsoleAuditLogger ressemble à ceci:

ConsoleAuditLogger.java


package org.springframework.security.acls.domain;

import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.AuditableAccessControlEntry;

import org.springframework.util.Assert;

public class ConsoleAuditLogger implements AuditLogger {

    public void logIfNeeded(boolean granted, AccessControlEntry ace) {
        Assert.notNull(ace, "AccessControlEntry required");

        if (ace instanceof AuditableAccessControlEntry) {
            AuditableAccessControlEntry auditableAce = (AuditableAccessControlEntry) ace;

            if (granted && auditableAce.isAuditSuccess()) {
                System.out.println("GRANTED due to ACE: " + ace);
            }
            else if (!granted && auditableAce.isAuditFailure()) {
                System.out.println("DENIED due to ACE: " + ace);
            }
        }
    }
}
  • 'L'interface AuditLoggerdéfinit la méthodelogIfNeeded (boolean, AccessControlEntry)`. --Le premier argument reçoit un indicateur, que l'autorisation ait été accordée ou refusée.
  • Le deuxième argument est l'objet ʻAccessControlEntry qui contient les informations d'autorisation. --Pour obtenir le paramètre (ʻAUDIT_SUCCESS, ʻAUDIT_FAILURE) indiquant s'il faut sortir le journal d'audit, utilisez la méthode ʻisAuditSuccess () ou ʻisAuditFailure () de ʻAuditableAccessControlEntry (Argument ʻAccessControlEntry ". Vous devez lancer `)
  • En fait, je pense qu'il est nécessaire de sortir dans un fichier journal, donc je sens que je vais implémenter ma propre classe ʻAuditLogger` en faisant référence à cette implémentation.

Définir à partir du programme

import org.springframework.security.acls.model.AuditableAcl;

...

    ObjectIdentityImpl objectIdentity = new ObjectIdentityImpl(Foo.class, 44L);
    AuditableAcl acl = (AuditableAcl) this.aclService.readAclById(objectIdentity);
    acl.updateAuditing(0, true, false);
    this.aclService.updateAcl(acl);
  • Pour changer par programme ʻAUDIT_SUCCESS et ʻAUDIT_FAILURE, utilisez la méthode ʻupdateAuditing (int, boolean, boolean) de ʻAuditableAcl.
  • Le premier argument est l'index pour spécifier ʻAccessControlEntry` que vous souhaitez modifier.
  • La valeur à définir dans ʻAUDIT_SUCCESS` comme deuxième argument
  • La valeur à définir dans ʻAUDIT_FAILURE` comme troisième argument

Définissez vos propres autorisations

Par défaut, seules cinq permissions sont disponibles: read, write, create, delete et ʻadministration`, mais vous pouvez définir des permissions supplémentaires de votre choix.

Essayez de faire 32 ( 100000 en notation binaire).

la mise en oeuvre

MyPermission.java


package sample.spring.security.acl;

import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.model.Permission;

public class MyPermission extends BasePermission {
    public static final Permission HOGE = new MyPermission(0b100000, 'H');

    private MyPermission(int mask, char code) {
        super(mask, code);
    }
}

--Créez votre propre classe Permission en héritant de BasePermission --Créez une constante appelée «HOGE», définissez le masque sur «0b100000» («32» en décimal) et définissez le code sur «H» pour définir l'instance.

insert_acl_tables.sql


INSERT INTO ACL_CLASS (ID, CLASS)
VALUES (100, 'sample.spring.security.domain.Foo');

INSERT INTO ACL_SID (ID, PRINCIPAL, SID)
VALUES (9, true, 'hoge');
INSERT INTO ACL_SID (ID, PRINCIPAL, SID)
VALUES (98, false, 'READONLY');
INSERT INTO ACL_SID (ID, PRINCIPAL, SID)
VALUES (99, false, 'HOGE');

INSERT INTO ACL_OBJECT_IDENTITY (ID, OBJECT_ID_CLASS, OBJECT_ID_IDENTITY, PARENT_OBJECT, OWNER_SID, ENTRIES_INHERITING)
VALUES (999, 100, 44, NULL, 9, false);

INSERT INTO ACL_ENTRY (ID, ACL_OBJECT_IDENTITY, ACE_ORDER, SID, MASK, GRANTING, AUDIT_SUCCESS, AUDIT_FAILURE)
VALUES (10, 999, 0, 98, 1, true, false, false);
INSERT INTO ACL_ENTRY (ID, ACL_OBJECT_IDENTITY, ACE_ORDER, SID, MASK, GRANTING, AUDIT_SUCCESS, AUDIT_FAILURE)
VALUES (11, 999, 1, 99, 32, true, false, false);
  • Accorder la permission de lecture à l'autorité "READONLY"
  • Accorder la permission 32 ( HOGE défini dans MyPermission) à l'autorité HOGE

MyAclSampleService.java


package sample.spring.security.service;

import org.springframework.security.access.prepost.PreAuthorize;
import sample.spring.security.domain.Foo;

public class MyAclSampleService {

    @PreAuthorize("hasPermission(#foo, read)")
    public void read(Foo foo) {
        System.out.println(foo);
    }
    
    @PreAuthorize("hasPermission(#foo, 'hoge')")
    public void hoge(Foo foo) {
        System.out.println(foo);
    }
}

--Vérifiez les autorisations avec read et hoge respectivement

MyAclServlet.java


package sample.spring.security.servlet;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import sample.spring.security.domain.Foo;
import sample.spring.security.service.MyAclSampleService;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@WebServlet("/acl")
public class MyAclServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        MyAclSampleService service = this.findServiceBean(req);
        Foo foo = new Foo(44L);

        this.callMethod("read", () -> service.read(foo));
        this.callMethod("hoge", () -> service.hoge(foo));
    }
        
    private void callMethod(String method, Runnable runnable) {
        try {
            System.out.println(method);
            runnable.run();
        } catch (AccessDeniedException e) {
            System.out.println(e.getMessage());
        }
    }
    
    ...
}

applicationContext.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:sec="http://www.springframework.org/schema/security"
       ...>

    ...
    
    <bean id="permissionFactory" class="org.springframework.security.acls.domain.DefaultPermissionFactory">
        <constructor-arg value="sample.spring.security.acl.MyPermission" />
    </bean>
    
    <bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
        <property name="permissionEvaluator">
            <bean class="org.springframework.security.acls.AclPermissionEvaluator">
                <constructor-arg ref="aclService" />
                <property name="permissionFactory" ref="permissionFactory" /> ★
            </bean>
        </property>
    </bean>

    <bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
        <constructor-arg ref="dataSource" />
        <constructor-arg ref="aclCache" />
        <constructor-arg ref="aclAuthorizationStrategy" />
        <constructor-arg ref="permissionGrantingStrategy" />
        <property name="permissionFactory" ref="permissionFactory" /> ★
    </bean>
    
    ...

    <sec:authentication-manager>
        <sec:authentication-provider>
            <sec:user-service>
                <sec:user name="foo" password="foo" authorities="READONLY" />
                <sec:user name="bar" password="bar" authorities="HOGE" />
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>
</beans>

--Définissez DefaultPermissionFactory comme un bean --Spécifiez l'objet Class de votre proprePermission ( MyPermission) dans l'argument du constructeur --Définissez le Bean de DefaultPermissionFactory sur ʻAclPermissionEvaluator et BasicLookupStrategy` comme propriétés, respectivement.

Contrôle de fonctionnement

Connectez-vous en tant qu'utilisateur foo et accédez à / acl

Sortie de la console


read
Foo{id=44}
hoge
Access is denied

Connectez-vous en tant qu'utilisateur bar et accédez à / acl

Sortie de la console


read
Access is denied
hoge
Foo{id=44}

La description

Définition de votre propre classe d'autorisation

MyPermission.java


package sample.spring.security.acl;

import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.model.Permission;

public class MyPermission extends BasePermission {
    public static final Permission HOGE = new MyPermission(0b100000, 'H');

    private MyPermission(int mask, char code) {
        super(mask, code);
    }
}

--Si vous voulez définir vos propres permissions, créez une classe qui hérite de BasePermission et définissez les permissions que vous voulez ajouter avec des constantes.

  • Le premier argument du constructeur est la valeur de bit correspondant à cette autorisation, et le second argument spécifie la représentation en une lettre de l'autorisation.
  • Le nom de la constante est important et le nom de la constante spécifié ici devient le nom de l'autorisation spécifiée dans le deuxième argument de l'expression hasPermission ().

Remplacer la classe d'autorisation existante

applicationContext.xml


    <bean id="permissionFactory" class="org.springframework.security.acls.domain.DefaultPermissionFactory">
        <constructor-arg value="sample.spring.security.acl.MyPermission" />
    </bean>
    
    <bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
        <property name="permissionEvaluator">
            <bean class="org.springframework.security.acls.AclPermissionEvaluator">
                <constructor-arg ref="aclService" />
                <property name="permissionFactory" ref="permissionFactory" />
            </bean>
        </property>
    </bean>

    <bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
        <constructor-arg ref="dataSource" />
        <constructor-arg ref="aclCache" />
        <constructor-arg ref="aclAuthorizationStrategy" />
        <constructor-arg ref="permissionGrantingStrategy" />
        <property name="permissionFactory" ref="permissionFactory" />
    </bean>

--Si vous ne faites rien, la classe d'autorisation utilisera BasePermission

  • Vous devez le remplacer par votre propre classe d'autorisation «MyPermission» que vous avez créée précédemment.
  • La classe d'autorisation est directement utilisée par DefaultPermissionFactory, qui est la classe d'implémentation de l'interface PermissionFactory. --Spécifiez l'objet Class de la classe d'autorisation que vous souhaitez utiliser dans l'argument constructeur de cette classe
  • Les constantes définies dans la classe d'autorisation à l'intérieur de DefaultPermissionFactory sont acquises par réflexion.
  • Puisque PermissionFactory est utilisé dans les classes ʻAclPermissionEvaluator et BasicLookupStrategy, remplacez leurs propriétés permissionFactory`.

Utilisation dans la formule

MyAclSampleService.java


    @PreAuthorize("hasPermission(#foo, 'hoge')")
    public void hoge(Foo foo) {
        System.out.println(foo);
    }
  • Avec les paramètres jusqu'à ce point, vous pourrez utiliser vos propres autorisations. --Cependant, contrairement aux autres autorisations existantes, les autorisations uniques n'ont pas de constantes définies dans SecurityExpressionRoot.
  • Vous devez donc le mettre entre guillemets simples (`` '') et le spécifier sous forme de chaîne de caractères.
  • Si le nom de la constante défini dans la classe d'autorisation est entièrement en majuscules, il n'y a pas de problème car il n'est pas vraiment sensible à la casse. ――Ce que cela signifie, c'est qu'en réalité, il est vérifié dans sa forme originale (hoge), et sinon, il est vérifié dans toutes les lettres majuscules ( HOGE).

référence

Recommended Posts