[JAVA] Spring Security Usage Memo Domänenobjektsicherheit (ACL)

Grundlegende und systematische Geschichte Zertifizierungs- / Autorisierungsgeschichte Remember-Me-Geschichte CSRF-Geschichte Session Management Story Geschichte des Antwortheaders Method Security Story CORS-Geschichte Die Geschichte von Run-As Teststory Sprechen Sie über die Zusammenarbeit mit MVC und Boot

Sonderedition Was Spring Security kann und was nicht

Was ist die Sicherheit von Domänenobjekten?

Die Autorisierungsverarbeitung mit hasAuthority () usw. wird grundsätzlich in Funktionseinheiten gesteuert. (Eine bestimmte Funktion (Bildschirm) muss die Berechtigung XX usw. haben.)

Beim tatsächlichen Erstellen eines Systems ist es jedoch selten erforderlich, Berechtigungen datenweise zu verwalten. Beispielsweise können nur Personen, die derselben Gruppe angehören wie die Person, die die Daten erstellt hat, die Daten sehen, oder nur der Ersteller oder Systemadministrator kann die Daten aktualisieren.

Spring Security bietet einen Mechanismus, um eine solche Zugriffskontrolle datenweise zu realisieren.

Dies wird als Domänenobjektsicherheit oder ACL (Access Control List) bezeichnet.

Hello World

Implementierung

Abhängigkeiten

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'★ Ergänzung
    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);

Gemeinsame Implementierung zur Bestätigung

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);
    }
}

--Standardausgabe von "foo" als Argument erhalten

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);
    }
}

--Nach der Ausgabe der Informationen des aktuellen Principals erstellen Sie eine Instanz von Foo mit id = 44 und id = 45 und führen dielogic ()Methode von MyAclSampleService aus.

Aufbau

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());
    }
}

Funktionsprüfung

Melden Sie sich als foo Benutzer an und greifen Sie / acl zu.

Ausgabe der Serverkonsole


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

Sowohl "id = 44" als auch "id = 45" wurde der Zugriff verweigert.

Melden Sie sich dann als "bar" -Benutzer an und greifen Sie auf "/ acl" zu.

Ausgabe der Serverkonsole


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

id = 44 hat dielogic ()Methode erfolgreich ausgeführt, und id = 45 hat den Zugriff verweigert.

Wie es funktioniert

Tabelle

Die folgenden vier werden angezeigt.

Die Tabellenstruktur sieht wie folgt aus.

ACLテーブル構造.png

Der Pfeil steht für FK, die Quelle des Pfeils ist die Referrer-Tabelle und die Spitze des Pfeils ist die Referrer-Tabelle.

Die Bedeutung jeder Tabelle und Spalte ist wie folgt.

ACL_CLASS

Säule Bedeutung
CLASS Java-Klassenname (FQCN) des Domänenobjekts

ACL_SID

Säule Bedeutung
PRINCIPAL Ein Flag, das unterscheidet, ob dieser Datensatz ein Principal ist
true→ Auftraggeber
falseGrantedAuthority
SID Eine Zeichenfolge, die die SID darstellt.
Wenn Sie ein Schulleiter sindusernameGrantedAuthorityDann wird der Zeichenfolgenausdruck gesetzt

ACL_OBJECT_IDENTITY

Säule Bedeutung
OBJECT_ID_CLASS Eine Spalte, die auf eine Klasse von Domänenobjekten verweist.
ACL_CLASSExtern verwiesen auf.
OBJECT_ID_IDENTITY ID, die die Instanz identifiziert
PARENT_OBJECT ElternACL_OBJECT_IDENTITYICH WÜRDE
OWNER_SID Erstellt diese InstanzACL_SIDICH WÜRDE
ENTRIES_INHERITING Flags, ob diese Instanz Berechtigungsvererbungsbeziehungen zu anderen Instanzen hat

ACL_ENTRY

Säule Bedeutung
ACL_OBJECT_IDENTITY Wenden Sie diese Berechtigungsdefinition anACL_OBJECT_IDENTITYICH WÜRDE
ACE_ORDER EinerACL_OBJECT_IDENTITYZu mehrfachACL_ENTRTYBestellen, wann verknüpft ist
SID Wenden Sie diese Berechtigungsdefinition anACL_SIDICH WÜRDE
MASK Ganzzahliger Wert, der die Berechtigungsdefinition darstellt (Details werden später beschrieben)
GRANTING Flag "Grant" oder "Reject"
AUDIT_SUCCESS Flag zur Ausgabe des Überwachungsprotokolls, wenn diese Berechtigungsdefinition erteilt wird
AUDIT_FAILUER Flag zur Ausgabe des Überwachungsprotokolls, wenn diese Berechtigungsdefinition verweigert wird

Hallo Weltdaten

Die in Hello World verwendeten Daten hatten eine Struktur wie ↓.

ACL_HelloWorld_データ.png

Typ der Domänenobjektkennung

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.

(Übersetzung) Wir beabsichtigen nicht, ** nicht lange Bezeichner im Spring Security ACL-Modul zu unterstützen **. long ist bereits mit allen Datenbanksequenzen kompatibel, ist der häufigste Datentyp und lang genug, um alle gängigen Verwendungsszenarien zu speichern.

27.3 Getting Started

Bean-Einstellungen zum Aktivieren der ACL

SecurityExpressionHandler-Erweiterung

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>

Definition von AclService

applicationContext.xml


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

--AclService bietet ACL-Funktionalität --JdbcAclService verwendet JDBC, um auf in der Datenbank definierte Informationen zuzugreifen und ACL-Funktionen zu implementieren.

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>

Wie der Cache funktioniert

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 und PermissionGrantingStrategy

--SpringCacheBasedAclCache speichert Objekte der Klasse AclImpl zwischen

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 implementiert die Acl -Schnittstelle und Acl erbt Serializable.

Übrigens muss "BasicLookupStrategy" auch Instanzen von "AclAuthorizationStrategy" und "PermissionGrantingStrategy" als Konstruktorargumente empfangen.

applicationContext.xml


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

Dies wird verwendet, um festzulegen, wann BasicLookupStrategy das AclImpl-Objekt aus den Datenbankinformationen rekonstruiert.

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 ist dafür verantwortlich, zu überprüfen, ob der aktuelle Principal diese Berechtigung hat, wenn er die ACL-Definition ändert. ―― Mit anderen Worten, es wird in dieser Hello World nicht wirklich verwendet

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 bietet einen Prozess, mit dem speziell ermittelt werden kann, ob der aktuelle Principal über die durchhasPermission ()angegebenen Berechtigungen verfügt.

AuditLogger --AuditLogger bietet einen Prozess zum Protokollieren des Ergebnisses der Berechtigungsbeurteilung. --ConsoleAuditLogger ist die einzige bereitgestellte Implementierungsklasse und gibt das Ergebnis an die Konsole aus.

Klassendiagramm, das die bisherige Geschichte zusammenfasst

spring-security.png

So was.

hasPermission()

MyAclSampleService.java


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

So erhalten Sie eine ID von einem Domänenobjekt

Nur nach ID angeben

MyAclSampleService.java


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

Berechtigungen angeben

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";

Definition von Berechtigungen

Definitionen von Berechtigungen ("Lesen", "Schreiben", "Erstellen", "Löschen", "Verwaltung") werden als ganzzahlige Werte ausgedrückt. In der Datenbank wird es in der Spalte "MASK" von "ACL_ENTRY" gespeichert.

Jede Berechtigung ist wie unten gezeigt jedem Binärbit zugeordnet.

5. Bit 4. Bit 3. Bit 2. Bit 1. Bit
administration delete create write read

Mit anderen Worten

Genehmigung Binäre Darstellung Dezimaldarstellung
read 00001 1
write 00010 2
create 00100 4
delete 01000 8
administration 10000 16

Sie fragen sich vielleicht: "Wenn Sie dann zwei Berechtigungen haben möchten," Lesen "und" Schreiben ", ist es" 00011 "(" 3 "in Dezimalzahl)?", Aber das ist nicht der Fall. Bis zum letzten wird eine der oben genannten fünf in MASK jedes ACL_ENTRY-Datensatzes eingegeben. Wenn Sie mehrere Berechtigungen erteilen, wird die Anzahl der Datensätze registriert. (Ist das eine Maske?)

Überprüfung

Eingabedaten


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) 

--Grant read (1), create (4) undadministrative (16)Berechtigungen für AUTHORITY_10101 einzeln.

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);
    }
}

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

** Funktionsprüfung **

Serverausgabe beim Anmelden mit 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}

Serverausgabe beim Anmelden mit Leiste


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 und delete wurden ebenfalls blockiert

Warum die Maske keine Bitmaske ist

Der Grund wurde in Dieses Problem auf GitHub beschrieben.

Ich bin skeptisch, dass es mir passt, weil meine Englischkenntnisse gering sind, aber ich fühle mich wie folgt.

  1. Wenn Sie die Berechtigung eines Elternteils mit der Berechtigung eines Kindes verweigern, wenn Sie die Berechtigungsvererbung verwenden, scheint es schwierig zu sein, festzustellen, welche Berechtigung mit einer Bitmaske verweigert wird.
  2. Bei der Suche mit SQL erschwert die Bitmaske die Suche.

Obwohl dort "MASKE" steht, fühlt es sich tatsächlich wie eine Division von ganzzahligen Werten an.

Immerhin: "Ich habe einen Erweiterungspunkt vorbereitet. Wenn Sie also wirklich mit einer Bitmaske urteilen möchten, verwenden Sie diesen." SEC-1166: Provide strategy interface for AclImpl isGranted() method.

ACL CRUD

Bis zu diesem Zeitpunkt wurden Daten wie "ACL_ENTRY" registriert, indem zuvor eine INSERT-Anweisung in SQL vorbereitet wurde.

Es ist jedoch schwierig, diese Tabellen direkt in der tatsächlichen Entwicklung zu bearbeiten, da Sie die Spezifikationen richtig verstehen müssen.

Daher gibt es eine API zum Verwalten dieser Daten.

Registrieren Sie eine neue Instanz-ACL

Implementierung

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);
    }

    ...
}

Funktionsprüfung

Zugriff zum Ausführen von "MyAclServlet".

Ausgabe der Serverkonsole


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]

* Die Ausgabe von Acl wird tatsächlich in einer Zeile ausgegeben, aber Zeilenumbrüche werden hinzugefügt, um die Anzeige zu vereinfachen </ font>

Erläuterung

Transaktionsaktivierung

--Annotationsbasierte deklarative Transaktionen sind aktiviert, da Sie die Transaktion beim Aktualisieren der ACL-Tabelle steuern müssen.

MyAclSampleService.java


package sample.spring.security.service;

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

@Transactional
public class MyAclSampleService {
...
  • Klasse mit @ Transactional notieren

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>

    ...
  • Aktivieren Sie annotationsbasierte Transaktionen mit <annotationsgesteuert>
  • Registrieren Sie die Implementierung von "PlatformTransactionManager" ("DataSourceTransactionManager") unter dem Namen "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);
    }
  • Aktivieren Sie die Transaktionsverwaltung mit "@ EnableTransactionManagement"
  • Registrieren Sie "DataSourceTransactionManager", eine Implementierung von "PlatformTransactionManager", als Bean.

Definition von 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);
    }
  • Es gibt eine Klasse namens "JdbcMutableAclService" mit einer Methode zum Aktualisieren von ACLs als Unterklasse von "JdbcAclService".
  • Registrieren Sie ein Objekt dieser Klasse als Bean anstelle von "JdbcAclService"
  • Sie übergeben "AclCache" im Konstruktorargument, was erforderlich ist, da "JdbcMutableAclService" diese Informationen auch aus dem Cache entfernt, wenn Sie den Vorgang zum Entfernen der ACL ausführen.

Registrierung von 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);
    }
}

--Erstellen Sie ein Objekt mit ObjectIdentity ( ObjectIdentityImpl)

  • Der erste Konstruktor von "ObjectIdeneityImpl" ist das "Class" -Objekt des Domänenobjekts.
  • Die zweite ist die Domänenobjektkennung (ID)
  • Übergeben Sie die erstellte "ObjectIdentity" an die "createAcl (ObjectIdentity)" -Methode von "MutableAclService"
  • Informationen werden in ACL_OBJECT_IDENTITY gespeichert, wenn createAcl () ausgeführt wird. --ACL_CLASS wird auch zusammen erstellt, wenn die Informationen nicht vorhanden sind
  • Zurückgegebenes MutableAcl
Inhaber

Ausgabe der Serverkonsole


[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]
  • Wenn Sie genau hinschauen, wird die "OWNER_SID" der erstellten "ACL_OBJECT_IDENTITY" auf "ACL_SID" von "SID = foo" gesetzt. --Dies wird in den Informationen des angemeldeten Benutzers (Principals) aufgezeichnet, wenn createAcl () von MutableAclService ausgeführt wird.

ACL Java-Klasse

ACL-Informationen werden in der folgenden Klassenstruktur modelliert.

ACLのクラス図.png

  • ObjectIdentity
  • Ein Objekt, das die Kennung eines Domänenobjekts darstellt
  • Sie können sich auf die Domänenobjektkennung und den Klassennamen beziehen.
  • Acl
  • Das Hauptobjekt von ACL
  • Eine Zusammenfassung aller mit "ObjectIdentity" verknüpften Berechtigungen
  • AccessControlEntry
  • Ein Objekt, das die einzelnen Berechtigungen darstellt, die "Sid" für "Acl" zugewiesen wurden
  • Permission
  • Ein Objekt mit spezifischen Berechtigungsinformationen
  • Sid
  • Ein Objekt, das das Ziel darstellt, dem Berechtigungen zugewiesen sind
  • Weder Principal noch "GrantedAuthority"

Wenn "createAcl ()" von "MutableAclService" ausgeführt wird, werden "Acl", "AccessControlEntry", "Permission" und "Sid" in der Form konstruiert, die der durch das Argument angegebenen "ObjectIdentity" zugeordnet ist.

Suchen Sie nach vorhandenen ACLs

Implementierung

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();
    }
    
    ...
}

Funktionsprüfung

Ausgabe der Serverkonsole (createObjectIdeneity)()Ausgabe entfällt)


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
]

Erläuterung

MyAclSampleService.java


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

--AclService bietet eine Methode zur Suche nach Acl

  • "readAclById (ObjectIdentity)" kann verwendet werden, um nach "Acl" zu suchen, das mit "ObjectIdentity" verknüpft ist.

Berechtigungen hinzufügen

Implementierung

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() {
        //Suchen Sie nach der zu aktualisierenden ACL und
        ObjectIdentity objectIdentity = new ObjectIdentityImpl(Foo.class, 10L);
        MutableAcl acl = (MutableAcl) aclService.readAclById(objectIdentity);

        //Behörde"HOGE_AUTHORITY"Gewähren Sie CREATE-Berechtigungen für
        List<AccessControlEntry> entries = acl.getEntries();
        GrantedAuthoritySid grantedAuthoritySid = new GrantedAuthoritySid(new SimpleGrantedAuthority("HOGE_AUTHORITY"));
        acl.insertAce(entries.size(), BasePermission.CREATE, grantedAuthoritySid, true);

        //Schulleiter"test_user"Erteilen Sie WRITE die Erlaubnis zu
        PrincipalSid principalSid = new PrincipalSid("test_user");
        acl.insertAce(entries.size(), BasePermission.WRITE, principalSid, true);

        //ACL-Änderungen speichern
        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);
    }
    
    ...
}

Funktionsprüfung

Ausgabe der Serverkonsole


★ Vor dem Hinzufügen von Berechtigungen
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]

...

★ Nach dem Hinzufügen von Berechtigungen
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}

Erläuterung

MyAclSampleService.java


    public void addPermission() {
        //Suchen Sie nach der zu aktualisierenden ACL und
        ObjectIdentity objectIdentity = new ObjectIdentityImpl(Foo.class, 10L);
        MutableAcl acl = (MutableAcl) aclService.readAclById(objectIdentity);

        //Behörde"HOGE_AUTHORITY"Gewähren Sie CREATE-Berechtigungen für
        List<AccessControlEntry> entries = acl.getEntries();
        GrantedAuthoritySid grantedAuthoritySid = new GrantedAuthoritySid(new SimpleGrantedAuthority("HOGE_AUTHORITY"));
        acl.insertAce(entries.size(), BasePermission.CREATE, grantedAuthoritySid, true);

        //Schulleiter"test_user"Erteilen Sie WRITE die Erlaubnis zu
        PrincipalSid principalSid = new PrincipalSid("test_user");
        acl.insertAce(entries.size(), BasePermission.WRITE, principalSid, true);

        //ACL-Änderungen speichern
        this.aclService.updateAcl(acl);
        
        System.out.println("acl = " + acl);
    }
  • Sie können Berechtigungen zu "Acl" hinzufügen, indem Sie "insertAce (int, Permission, Sid, boolean)" von "MutableAcl" ausführen --Intern wird "AccessControlEntry" zu "Acl" hinzugefügt
  • Das erste "int" gibt die Einfügeposition der Berechtigung durch den Index an, der mit "0" beginnt. --Permissions (AccessControlEntry) werden in Acl als List gespeichert und an das erste Argument der add (int, E) -Methode übergeben.
  • Wenn Sie also einen Wert kleiner als "0" oder größer als "size ()" übergeben, wird ein Fehler angezeigt (wenn er mit "size ()" identisch ist, wird er am Ende hinzugefügt).
  • In DB wird es zu "ACE_ORDER" von "ACL_ENTRY" --Permission kann die in der Implementierungsklasse BasePermission definierten Konstanten angeben --Sid gibt GrantedAuthoritySid oder PrincipalSid an
  • Wenn Sie den Berechtigungen "GrantedAuthoritySid" Berechtigungen zuweisen möchten --Verwenden Sie PrincipalSid, um Principals Berechtigungen zuzuweisen
  • Der letzte "Boolesche Wert" gibt an, ob die Berechtigung "erteilt" oder "verweigert" werden soll.
  • "wahr" wird gewährt, "falsch" wird abgelehnt

Verweigern Sie Berechtigungen und ACE_ORDER

Implementierung

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 + ")");
    }
}
  • Beim ersten Mal kann "readAclById ()" die ACL nicht abrufen und "NotFoundException" wird ausgelöst. Registrieren Sie die Berechtigung daher nur zu diesem Zeitpunkt.
  • Registrieren Sie die Berechtigung "Lesen" mit "Verweigern" in der Berechtigung "DENIED_READ".
  • Für die Berechtigung "PERMIT_READ" ist die Berechtigung "Lesen" bei "Grant" registriert.
  • Außerdem muss DENIED_READ auf 0 gesetzt werden, damit es an erster Stelle steht.
  • Tun Sie nichts, wenn ACL bereits registriert ist
  • Außerdem wird die Methode "read ()" mit "@ PreAuthorize" versehen, und "hasPermission ()" prüft, ob Sie über "Leseberechtigungen" verfügen.
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());
        }
    }
    
    ...
}
  • Geben Sie die Informationen des aktuellen Principals aus (printPrincipal ())
  • Registrieren Sie ACL (addPermission ())
  • Geben Sie die in der Tabelle registrierten Informationen aus (printTables ())
  • Führen Sie die Methode "read ()" aus und geben Sie eine Nachricht aus, wenn "AccessDeniedException" ausgelöst wird

Implementierung

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>
  • Definieren Sie zwei Benutzer, foo und bar
  • Setzen Sie die Berechtigung PERMIT_READ für den Benutzer foo
  • Setzen Sie die Berechtigungen "PERMIT_READ" und "DENIED_READ" für den Benutzer "bar"

Funktionsprüfung

Melden Sie sich als foo Benutzer an und greifen Sie / acl zu.

Ausgabe der Serverkonsole


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})

Die Methode read () kann ausgeführt werden

Melden Sie sich dann als "bar" -Benutzer an und greifen Sie auf "/ acl" zu.

Ausgabe der Serverkonsole


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

Zugriff wurde verweigert.

Erläuterung

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 scheint angewendet zu werden, wobei der Bestellung zuerst Priorität eingeräumt wird

  • Der Benutzer "bar" wurde ebenfalls auf "PERMIT_READ" gesetzt, aber die Berechtigung wurde als "verweigert" eingestuft, da "DENIED_READ" mit "ACE_ORDER" vor "PERMIT_READ" stand.

Wo kann man es verwenden?

――Wenn ich diesen Mechanismus zum ersten Mal sah (Erlaubnis verweigern und "ACE" bestellen), fiel mir nicht viel Nutzen ein.

  • Wenn Sie nur den Zugriff verweigern möchten, müssen Sie keine Erlaubnis erteilen, sodass ich nicht wusste, wann ich die Erlaubnis "verweigern" soll. ――Das einzige, was ich mir ausgedacht habe, war, wie man die Autorität "total leugnen" wie ↑ definiert
  • Wenn Sie am Ende die Berechtigung "Typ" und am Anfang die Berechtigung "Verweigern" hinzufügen, hat die Berechtigung "Verweigern" immer Vorrang. ――Und wenn Sie der Berechtigung "Verweigern" die Berechtigung "Verweigern" zuordnen ("GrantedAuthority"), verhält sie sich wie "Grundsätzlich können Sie nicht zugreifen, wenn Sie diese Berechtigung erhalten". ――Ich weiß nicht, ob es eine solche Situation gibt, aber wenn Sie den Zugriff grundsätzlich verweigern möchten, können Sie Dinge wie "Sie können den Zugriff deaktivieren, indem Sie diese Berechtigung festlegen" steuern.

Berechtigungsprüfungen für ACL-Aktualisierungen

Nicht jeder kann die ACL aktualisieren.

Implementierung

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 user admin(16)Erlaubnis erteilen

--Erstelle ACL_OBJECT_IDENTITY für Foo Objekt mit ID = 10 --Stellen Sie den "Hoge" -Prinzipal als Eigentümer ein --Grant Administration Berechtigungen zu Admin Principals

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>
  • Übergabe von PERMISSION_MANAGER im Konstruktorargument von AclAuthorizationStrategyImpl --hoge, fuga, admin Benutzer hat keine Berechtigungen --Grant PERMISSION_MANAGER Privilegien nur für Piyo Benutzer

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);
    }
}

--Suchen nach einem Foo-Objekt mit id = 10 und Hinzufügen von Berechtigungen mit 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());
        }
    }
    
    ...
}
  • Gibt die angemeldeten Benutzerinformationen und den Datenbankstatus vor der Verarbeitung aus
  • Nachdem Sie die Methode "addPermission ()" von "MyAclSampleService" ausgeführt haben, geben Sie den Status der Datenbank erneut aus. --Wenn bei addPermission () eine Ausnahme auftritt, werden diese Informationen ausgegeben.

Funktionsprüfung

** Beim Zugriff als Hoge-Benutzer **

Ausgabe der Serverkonsole


★ Benutzerinformationen
name=hoge
authorities=

★ Status der Tabelle vor dem Update
[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}

★ Methodenausführung
service.addPermission()

★ Status der Tabelle nach dem Update
[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}
  • Berechtigungen wurden erfolgreich hinzugefügt

** Beim Zugriff als Fuga-Benutzer **

Ausgabe der Serverkonsole


★ Benutzerinformationen
name=fuga
authorities=

★ Status der Tabelle vor dem Update
[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}

★ Methodenausführung
service.addPermission()

★ Fehlerinformationen
e.class = class org.springframework.security.acls.model.NotFoundException, message = Unable to locate a matching ACE for passed permissions and SIDs
  • Berechtigungen konnten nicht hinzugefügt werden und eine Ausnahme (NotFoundException) wurde ausgelöst

** Beim Zugriff als Piyo-Benutzer **

Ausgabe der Serverkonsole


★ Benutzerinformationen
name=piyo
authorities=PERMISSION_MANAGER

★ Status der Tabelle vor dem Update
[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}

★ Methodenausführung
service.addPermission()

★ Status der Tabelle nach dem Update
[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}
  • Das Berechtigungsupdate ist erfolgreich

** Beim Zugriff als Administrator **

Ausgabe der Serverkonsole


★ Benutzerinformationen
name=admin
authorities=

★ Status der Tabelle vor dem Update
[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}

★ Methodenausführung
service.addPermission()

★ Status der Tabelle nach dem Update
[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}
  • Das Berechtigungsupdate ist erfolgreich

Erläuterung

  • Die folgenden Faktoren spielen bei der Aktualisierung der Berechtigungen eine Rolle:
  • Ob Sie der Eigentümer der ACL sind, die Sie aktualisieren möchten
  • Ob die ACL, die Sie aktualisieren möchten, über Administratorrechte verfügt
  • Ob Sie über die im Konstruktorargument "AclAuthorizationStrategyImpl" angegebenen Berechtigungen verfügen --ACL-Aktualisierungstyp (allgemein, im Besitz, geprüft) --Und es wird beurteilt, ob es in der folgenden Reihenfolge aktualisiert werden kann
  1. Für den Eigentümer der ACL, den der aktuelle Principal zu aktualisieren versucht, Zulassen </ font>, wenn der ACL-Aktualisierungstyp "Allgemein" oder "Besitz" ist.
  2. Andernfalls lassen Sie </ font> zu, wenn Sie über die in AclAuthorizationStrategyImpl angegebenen Berechtigungen verfügen
  3. Andernfalls lassen Sie </ font> zu, wenn die ACL, die Sie aktualisieren möchten, über Administratorrechte verfügt.
  4. Andernfalls ist nicht zulässig </ font>

Aktualisierungstyp

Es gibt drei Arten von ACL-Updates:

--Allgemeines

  • Besessen --Prüfung

Die Methoden zum Aktualisieren der ACL sind wie folgt klassifiziert.

Methode Aktualisierungstyp
insertAce() Allgemeines
updateAce() Allgemeines
deleteAce() Allgemeines
setParent() Allgemeines
setEntriesInheriting() Allgemeines
setOwner() Im Besitz
updateAuditing() Prüfung

Diese drei Typen werden übrigens in der Schnittstelle "AclAuthorizationStrategy" definiert.

AclAuthorizationStrategy.java


public interface AclAuthorizationStrategy {

	int CHANGE_OWNERSHIP = 0;Im Besitz
	int CHANGE_AUDITING = 1;Prüfung
	int CHANGE_GENERAL = 2;Allgemeines

In AclAuthorizationStrategyImpl angegebene Berechtigungen

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>
  • Die im Konstruktor von "AclAuthorizationStrategyImpl" übergebene "GrantedAuthority" wird beim Aktualisieren der ACL verwendet. --Wenn der angemeldete Benutzer, der versucht zu aktualisieren, über die hier angegebene Berechtigung verfügt, ist die Aktualisierung zulässig.
Geben Sie Berechtigungen für jeden Aktualisierungstyp an

Im obigen Beispiel wird nur die Berechtigung "PERMISSION_MANAGER" angegeben. In diesem Fall sind alle Arten von Aktualisierungen (allgemein, im Besitz, geprüft) zulässig, wenn Sie über diese Berechtigung verfügen.

Wenn Sie steuern möchten, dass ●● für das "allgemeine" Update erforderlich ist, ist ▲▲ für das "eigene" Update und ★★ für das "Audit" wie folgt erforderlich. Definieren Sie eine Bohne in.

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>
  • Setzen Sie die Anzahl der an das Konstruktorargument übergebenen "GrantedAuthority" auf 3 (beachten Sie, dass 2 oder 4 zu einem Fehler führen).
  • Wie unten gezeigt, bestimmt die Position der angegebenen "GrantedAuthority", welchem Aktualisierungstyp die Berechtigung zugeordnet ist.
  1. Geben Sie die Berechtigungen an, die für "eigene" Updates erforderlich sind
  2. Geben Sie die Berechtigungen an, die für Aktualisierungen im Zusammenhang mit "Audit" erforderlich sind.
  3. Geben Sie die Berechtigungen an, die für "allgemeine" Updates erforderlich sind

Informationen zum Verhalten, wenn das Update abgelehnt wird

Im obigen Beispiel wurde eine "NotFoundException" ausgelöst, wenn das Update abgelehnt wurde.

Es ist nicht wahr, dass "NotFoundException" immer ausgelöst wird, wenn es nicht erlaubt ist. Wenn für die zu aktualisierende ACL die Berechtigung "Administration" auf "Verweigern" gesetzt ist, wird eine "AccessDeniedException" ausgelöst.

Dies ist tatsächlich ein ** Fehler **, und in beiden Fällen ist es richtig, eine "AccessDeniedException" auszulösen.

Eine Suche nach einem Problem auf GitHub zeigt, dass dieser Fehler aufgetreten ist. Ab dem 09. Juli 2017 bleibt dieses Problem jedoch offen und scheint nicht behoben zu sein. Es scheint, dass dieses Problem im Jahr 2009 erkannt wurde, sodass es vollständig vernachlässigt wurde (nicht darauf beschränkt, alle ACL-bezogenen Probleme wurden lange Zeit vernachlässigt und werden möglicherweise nicht viel gewartet. Unbekannt).

Darüber hinaus bezieht sich die Berechtigungsprüfung für die ACL-Aktualisierung bei Verwendung von Rollenhierarchien nicht auf die übergeordnete Rolle [https://github.com/spring-projects/spring-security/issues]. Bugs wie / 4186) wurden ausgelöst (auch lange Zeit unbeaufsichtigt gelassen).

Wenn Sie diesen Bereich mit korrektem Betrieb verwenden möchten, müssen Sie das vorhandene "AclAuthorizationStrategyImpl" kopieren, eine eigene Implementierungsklasse erstellen, die das Problem behebt, und es anstelle von "AclAuthorizationStrategyImpl" verwenden. (Oder ziehen Sie die geänderte Version an die Kopffamilie)

Geben Sie Berechtigungen eine Vererbungsbeziehung

"ACL_OBJECT_IDENTITY" kann eine Vererbungsbeziehung haben.

Hello World

Implementierung

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);
  • Definiert ACL_OBJECT_IDENTITY von id = 44 ... 47 --Granting "Lese" -Zulassung zu "READONLY" -Erlaubnis nur für "id = 44" --id = 45 gibt kein übergeordnetes Objekt usw. an. --id = 46 gibt nur die ID des übergeordneten Objekts mit PARENT_OBJECT an (ENTRIES_INHERITING ist false) --id = 47 setzt ENTRIES_INHERITING nach Angabe des übergeordneten Objekts auf true

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 + ")");
    }
}

--Überprüfen Sie, ob Sie Leseberechtigungen für das Foo-Objekt haben, das Sie als Argument erhalten

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());
        }
    }
    
    ...
}

--Erstelle ein Foo Objekt mit id = 44 ... 47 und führe dieread ()Methode von MyAclSampleService aus

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>

--Grant READONLY Privilegien für den foo Benutzer

Funktionsprüfung

Melden Sie sich als foo Benutzer an und greifen Sie / acl zu.

Ausgabe der Serverkonsole


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})
  • Nur "id = 44, 47" kann die Methode ausführen, "id = 45, 46" wird abgelehnt

Erläuterung

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);
  • Fügen Sie die folgenden zwei Einstellungen zu "ACL_OBJECT_IDENTITY" hinzu, um die Vererbung von Berechtigungen zu gewährleisten.
  • Setzen Sie PARENT_OBJECT auf ID des übergeordneten ACL_OBJECT_IDENTITY
  • Setzen Sie ENTRIES_INHERITING auf true
  • Dadurch kann die Berechtigung rückwirkend für den Elternteil festgelegt werden, wenn die Berechtigung auf die "ACL_OBJECT_IDENTITY" des Kindes überprüft wird.

Als Standardberechtigung verwenden

  • Durch die Verwendung der Berechtigungsvererbung können Sie beispielsweise "Standardberechtigungen" erreichen und Definitionsinformationen an einem Ort verwalten.
  • ** Es ist ein Rätsel, ob es für die Verwendung korrekt ist **, aber "OBJECT_IDENTITY" von "ACL_OBJECT_IDENTITY" kann registriert werden, auch wenn es keine vorhandene "ID" ist.
  • Wenn Sie "ACL_OBJECT_IDENTITY" mit einem unmöglichen Wert wie "OBJECT_IDENTITY = -1" registrieren und andere normale "ACL_OBJECT_IDENTITY" diesen Standard "ACL_OBJECT_IDENTITY" erben lassen, wird die Standardberechtigung festgelegt. realisierbar ――Jedoch, wenn die Vererbung in mehreren Ebenen geschichtet ist, scheint es schwierig zu sein, "alle in dieser" ACL_OBJECT_IDENTITY "festgelegten Berechtigungen allein durch SQL zu identifizieren (der Maximalwert der Ebene ist festgelegt). Wenn ja, ist es in Ordnung, wenn Sie für diese Nummer "LEFT JOIN" ausführen?) ――Ich denke, es ist sicherer, die Hierarchie so klein wie möglich zu halten.

Programmatisch Eltern setzen

Implementierung

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);
    }
    
    ...
}

Funktionsprüfung

Zugriff auf / acl

Serverprotokollausgabe


[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}

Erläuterung

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); //★ Eltern hier einstellen
        this.aclService.updateAcl(childAcl);
    }
  • Um eine übergeordnete ACL für eine ACL festzulegen, verwenden Sie die Methode "setParent ()" von "MutableAcl". --ENTRIES_INHERITING ist standardmäßig "true", wenn eine neue ACL erstellt wird. Daher muss diese beim Erstellen einer neuen ACL nicht angegeben werden (sie kann mit der Methode "setEntriesInheriting ()" festgelegt werden.)

Audit-Log

Es gibt einen Mechanismus zum Ausgeben der Informationen als Überwachungsprotokoll, wenn die Berechtigung erteilt oder verweigert wird.

Hello World

Implementierung

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);

--Für id = 44 von Foo die Berechtigungen" give read "und" verweigert write "für das Privileg" READONLY "festlegen. --ACL_ENTRY in "Grant read" ändert AUDIT_SUCCESS in true --ACL_ENTRY in "Reject write "setzt AUDIT_FAILURE auf 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>

Funktionsprüfung

Melden Sie sich als foo Benutzer an und greifen Sie / acl zu

Ausgabe der Serverkonsole


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
  • Vor dem Ausführen jeder Methode wird an die Konsole ausgegeben, ob die Berechtigung erteilt oder verweigert wurde.

Erläuterung

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);
  • Um die Ausgabe des Überwachungsprotokolls zu aktivieren, setzen Sie "AUDIT_SUCCESS" oder "AUDIT_FAILURE" in "ACL_ENTRY" auf "true". --Wenn "AUDIT_SUCCESS" "true" ist, wird ein Protokoll ausgegeben, wenn die Erteilung der Berechtigung festgelegt wird. --Wenn "AUDIT_FAILURE" "true" ist, wird ein Protokoll ausgegeben, wenn die Verweigerung der Berechtigung festgestellt wird.

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>
  • Die Ausgabe des Audit-Protokolls wird von einer Klasse durchgeführt, die die AuditLogger-Schnittstelle implementiert. --AuditLogger ist auf DefaultPermissionGrantingStrategy gesetzt
  • Die Klasse "ConsoleAuditLogger", die standardmäßig als Implementierung von "AuditLogger" bereitgestellt wird, gibt das Protokoll an die Standardausgabe aus.

Implementierung der Standardausgabe des Überwachungsprotokolls

Die Implementierung von ConsoleAuditLogger sieht folgendermaßen aus:

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);
            }
        }
    }
}
  • Die AuditLogger-Schnittstelle definiert die logIfNeeded-Methode (boolean, AccessControlEntry).
  • Dem ersten Argument wird ein Flag übergeben, ob die Erlaubnis erteilt oder verweigert wurde.
  • Das zweite Argument ist ein AccessControlEntry-Objekt, das die Berechtigungsinformationen enthält.
  • Um die Einstellungen ("AUDIT_SUCCESS", "AUDIT_FAILURE") für die Ausgabe des Überwachungsprotokolls abzurufen, verwenden Sie die Methode "isAuditSuccess ()" oder "isAuditFailure ()" von "AuditableAccessControlEntry" (das Argument "AccessControlEntry"). Du musst `)
  • Eigentlich denke ich, dass es notwendig ist, in eine Protokolldatei auszugeben, daher habe ich das Gefühl, dass ich meine eigene "AuditLogger" -Klasse implementieren werde, indem ich auf diese Implementierung verweise.

Aus dem Programm einstellen

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);
  • Um "AUDIT_SUCCESS" und "AUDIT_FAILURE" programmgesteuert zu ändern, verwenden Sie die "updateAuditing (int, boolean, boolean)" -Methode von "AuditableAcl".
  • Das erste Argument ist der Index zur Angabe des AccessControlEntry, den Sie ändern möchten.
  • Der Wert, der für das zweite Argument auf "AUDIT_SUCCESS" gesetzt werden soll
  • Der Wert, mit dem das dritte Argument auf "AUDIT_FAILURE" gesetzt wird

Definieren Sie Ihre eigenen Berechtigungen

Standardmäßig sind nur fünf Berechtigungen verfügbar: "Lesen", "Schreiben", "Erstellen", "Löschen" und "Verwaltung". Sie können jedoch zusätzliche eigene Berechtigungen definieren.

Versuchen Sie, "32" ("100000" in binärer Notation) zu machen.

Implementierung

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);
    }
}

--Erstellen Sie Ihre eigene Berechtigungsklasse, indem Sie BasePermission erben --Erstellen Sie eine Konstante namens "HOGE", setzen Sie die Maske auf "0b100000" ("32" in Dezimalzahl) und setzen Sie den Code auf "H", um die Instanz festzulegen.

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);

--Grant read Erlaubnis zur READONLY Autorität --Grant 32 ( HOGE definiert in MyPermission) Erlaubnis zur HOGE Autorität

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);
    }
}

--Überprüfen Sie die Berechtigungen mit "Lesen" bzw. "Hoge"

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>
  • Definieren Sie "DefaultPermissionFactory" als Bean
  • Geben Sie im Konstruktorargument das Class-Objekt Ihrer eigenenPermission ( MyPermission) an
  • Setzen Sie die Bean von "DefaultPermissionFactory" auf "AclPermissionEvaluator" bzw. "BasicLookupStrategy" als Eigenschaften.

Funktionsprüfung

Melden Sie sich als foo Benutzer an und greifen Sie / acl zu

Konsolenausgabe


read
Foo{id=44}
hoge
Access is denied

Melden Sie sich als bar Benutzer an und greifen Sie / acl zu

Konsolenausgabe


read
Access is denied
hoge
Foo{id=44}

Erläuterung

Definieren Sie Ihre eigene Berechtigungsklasse

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);
    }
}
  • Wenn Sie Ihre eigenen Berechtigungen definieren möchten, erstellen Sie eine Klasse, die "BasePermission" erbt, und definieren Sie die Berechtigungen, die Sie mit Konstanten hinzufügen möchten.
  • Das erste Argument des Konstruktors ist der dieser Berechtigung entsprechende Bitwert, und das zweite Argument gibt die Ein-Buchstaben-Darstellung der Berechtigung an.
  • Der Konstantenname ist wichtig, und der hier angegebene Konstantenname wird zum Namen der Berechtigung, die im zweiten Argument des Ausdrucks "hasPermission ()" angegeben ist.

Ersetzen Sie die vorhandene Berechtigungsklasse

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>
  • Wenn Sie nichts tun, verwendet die Berechtigungsklasse "BasePermission"
  • Sie müssen dies durch Ihre eigene Berechtigungsklasse "MyPermission" ersetzen, die Sie zuvor erstellt haben.
  • Die Berechtigungsklasse wird direkt von "DefaultPermissionFactory" verwendet, der Implementierungsklasse der "PermissionFactory" -Schnittstelle.
  • Geben Sie das Class-Objekt der Berechtigungsklasse an, die Sie im Konstruktorargument dieser Klasse verwenden möchten
  • Die in der Berechtigungsklasse in "DefaultPermissionFactory" definierten Konstanten werden durch Reflektion erfasst.
  • Da "PermissionFactory" in den Klassen "AclPermissionEvaluator" und "BasicLookupStrategy" verwendet wird, ersetzen Sie deren "PermissionFactory" -Eigenschaften.

In Formel verwenden

MyAclSampleService.java


    @PreAuthorize("hasPermission(#foo, 'hoge')")
    public void hoge(Foo foo) {
        System.out.println(foo);
    }
  • Mit den Einstellungen bis zu diesem Punkt können Sie Ihre eigenen Berechtigungen verwenden.
  • Im Gegensatz zu anderen vorhandenen Berechtigungen sind für eindeutige Berechtigungen jedoch keine Konstanten in "SecurityExpressionRoot" definiert. --So müssen Sie es in einfache Anführungszeichen (') setzen und als Zeichenfolge angeben. --Wenn der in der Berechtigungsklasse definierte Konstantenname nur in Großbuchstaben geschrieben ist, gibt es kein Problem, da die Groß- und Kleinschreibung nicht wirklich berücksichtigt wird. ――Was dies bedeutet, dass die Prüfung in Wirklichkeit in ihrer ursprünglichen Form ("hoge") durchgeführt wird und wenn nicht, in Großbuchstaben ("HOGE").

Referenz

Recommended Posts