[JAVA] Spring Security-Nutzungsnotiz Basic / Mechanismus

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

Sonderedition Was Spring Security kann und was nicht

Umgebung

Java 1.8.0_xxx

AP-Server

Tomcat 8.0.35

Gradle 3.2.1

OS Windows 10

Was ist Frühlingssicherheit?

Eines der Projekte von Spring. Ein Framework, das Webanwendungen hauptsächlich Sicherheitsfunktionen hinzufügt.

Es bietet Schutz vor verschiedenen bekannten Angriffen wie CSRF- und Sitzungsfixierungsangriffen.

Hello World Hier ist in etwas mehr Reihenfolge für die Ankündigung geschrieben. Es ist möglicherweise einfacher zu verstehen, wenn Sie es zusammen mit der Folie lesen (ich denke, dass es nur auf der Folie geschrieben wurde).

Implementierung

Ordnerstruktur


|-build.gradle
`-src/main/webapp/
  |-index.jsp
  `-WEB-INF/
    |-applicationContext.xml
    `-web.xml

build.gradle


apply plugin: 'war'

sourceCompatibility = '1.8'
targetCompatibility = '1.8'
compileJava.options.encoding = 'UTF-8'

repositories {
    mavenCentral()
}

dependencies {
    providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
    compile 'javax.servlet:jstl:1.2'
    compile 'org.springframework.security:spring-security-web:4.2.1.RELEASE'
    compile 'org.springframework.security:spring-security-config:4.2.1.RELEASE'
}

war.baseName = 'spring-security-sample'

web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

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

    <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="hoge" password="HOGE" authorities="ROLE_USER" />
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>
</beans>

index.jsp


<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Hello Spring Security!!</title>
    </head>
    <body>
        <h1>Hello Spring Security!!</h1>
        
        <c:url var="logoutUrl" value="/logout" />
        <form action="${logoutUrl}" method="post">
            <input type="submit" value="logout" />
            <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
        </form>
    </body>
</html>

Funktionsprüfung

Generieren Sie eine Kriegsdatei (spring-security-sample.war) mit gradle war und stellen Sie sie für Tomcat bereit.

Öffnen Sie nach Abschluss der Bereitstellung "http: // localhost: 8080 / spring-security-sample" in Ihrem Browser.

spring-security.jpg

Ein mysteriöser Anmeldebildschirm wird angezeigt.

Geben Sie "hoge" und "HOGE" in Benutzer und Passwort ein und klicken Sie auf die Schaltfläche "Anmelden".

spring-security.jpg

Der Inhalt von index.jsp wird angezeigt.

Klicken Sie abschließend auf die Schaltfläche "Abmelden".

spring-security.jpg

Die Abmeldung ist abgeschlossen und Sie kehren zum Anmeldebildschirm zurück.

Erläuterung

Abhängigkeiten

build.gradle


    compile 'org.springframework.security:spring-security-web:4.2.1.RELEASE'
    compile 'org.springframework.security:spring-security-config:4.2.1.RELEASE'

In der Mindestkonfiguration können Sie "spring-security-web" und "spring-security-config" als Abhängigkeiten hinzufügen (es scheint gut zu sein) (http://docs.spring.io/spring-security/site/docs/4.2.1). .RELEASE / reference / htmlsingle / #gradle).

spring-security-web

Es enthält Code für "Filter" und Web-Apps. Wenn Sie eine Webauthentifizierung oder eine URL-basierte Zugriffskontrolle benötigen, benötigen Sie dieses Modul. Das Hauptpaket ist "org.springframework.security.web"

spring-security-config

Es enthält Code zum Parsen des Namespace zum Schreiben von Definitionen in XML und Code für die Java-Konfiguration. Dieses Modul wird für XML-basierte Konfiguration und Java-Konfigurationen benötigt. Das Hauptpaket ist "org.springframework.security.config" Die hier enthaltenen Klassen werden von der Anwendung nicht direkt verwendet.

Federbehälterinitialisierung

web.xml


    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

Durch die Registrierung von "ContextLoaderListener" als Listener wird der Spring-Container ("ApplicationContext") initialisiert.

Wenn Sie nicht angeben, was für die Klasse "ApplicationContext" verwendet werden soll, wird standardmäßig "XmlWebApplicationContext" verwendet. Es wird in org / springframework / web / context / ContextLoader.properties von spring-web-x.x.x.RELEASE.jar wie folgt definiert und von ContextLoaderListener geladen, wenn der Servlet-Container initialisiert wird.

ContextLoader.properties


# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

XmlWebApplicationContext lädt standardmäßig WEB-INF / applicationContext.xml als Konfigurationsdatei.

Spring Security-Einstellungen

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: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="hoge" password="HOGE" authorities="ROLE_USER" />
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>
</beans>

applicationContext.xml selbst ist eine Standardkonfigurationsdatei für die Spring Bean-Definition, keine spezielle Konfigurationsdatei für Spring Security.

Durch Laden von "http: // www.springframework.org / schema / security" mit "xmlns" können Sie das Tag für Spring Security verwenden (verwenden Sie in der Referenz dieses Tag für Spring Security * Wir nennen es * Namespace **). Mit diesem Tag können Sie die Spring Security-Einstellungen für diese Datei konfigurieren.

Die obigen Einstellungen definieren ungefähr Folgendes:

Die einzelnen Tags werden im Folgenden etwas detaillierter beschrieben.


<http>

Wenn Sie das -Tag definieren, werden einige Beans automatisch im Container registriert. Unter diesen sind die folgenden zwei Klassen wichtige Bohnen.

  1. FilterChainProxy
  2. SecurityFilterChain

FilterChainProxy ist im Container mit dem Bean-Namen "springSecurityFilterChain" registriert. </ span> Diese Klasse ist das Gateway zur Spring Security-Verarbeitung.

SecurityFilterChain ist nur eine Schnittstelle für sich, und DefaultSecurityFilterChain ist im Container als Implementierungsklasse registriert. Wie der Name schon sagt, ist "SecurityFilterChain" eine Kette von "javax.servlet.Filter", die eine Sicherheitsfunktion hat und mehrere "Filter" enthält. Spring Security realisiert die Sicherheitsfunktion mithilfe von "Filter".

Wie Sie unter dem Namen "FilterChainProxy" sehen können, tut die Klasse selbst nichts. Die spezifische Verarbeitung wird an die Filter der SecurityFilterChain delegiert.


<intercept-url>

Die für den Zugriff erforderlichen Bedingungen (Zugriffskontrolle) werden für jedes URL-Muster definiert.

Das Attribut pattern kann im Ameisenpfadformat beschrieben werden.

Geben Sie im Attribut "Zugriff" die Bedingungen an, die für den Zugriff auf die im Attribut "Muster" angegebene URL erforderlich sind. allowAll bedeutet, den gesamten Zugriff zuzulassen (keine Authentifizierung erforderlich). Und "isAuthenticated ()" bedeutet, den Zugriff zuzulassen, wenn er authentifiziert (angemeldet) ist.

Das Attribut "access" wird in der Spring-eigenen Ausdruckssprache Spring Expression Language (SpEL) beschrieben.


<form-login>

Definiert, dass eine Formularauthentifizierung erforderlich ist.

Im Falle eines Authentifizierungsfehlers werden Sie standardmäßig zu "/ login" umgeleitet. Wenn Sie auf "/ login" zugreifen, gelangen Sie standardmäßig zu einer einfachen Anmeldeseite, die von Spring Security bereitgestellt wird.

Diese Standard-Anmeldeseite wird von "DefaultLoginPageGeneratingFilter" in "spring-security-web-x.x.x.RELEASE.jar" generiert.


<logout>

Durch Hinzufügen dieses Tags können Sie sich abmelden.

Standardmäßig werden Sie durch eine POST-Anforderung an "/ logout" abgemeldet.


<authentication-manager>

Definieren Sie "AuthenticationManager" als Bean. AuthenticationManager ist eine Klasse, die die Authentifizierungsverarbeitung ausführt und definiert werden muss.

AuthenticationManager selbst ist eine Schnittstelle, und ProviderManager wird für die Implementierungsklasse verwendet. ProviderManager selbst führt keine spezifische Authentifizierungsverarbeitung durch, delegiert jedoch die Authentifizierungsverarbeitung an AuthenticationProvider, der später beschrieben wird.


<authentication-provider>

Definieren Sie "AutheticationProvider" als Bean. Diese Schnittstelle bietet eine spezifische Authentifizierungsverarbeitung entsprechend der Art der Authentifizierung.

Die Klasse, die die LDAP-Authentifizierungsverarbeitung bereitstellt, ist beispielsweise "LdapAuthenticationProvider".

Wenn Sie dieses Tag deklarieren, wird "DaoAuthenticationProvider" im Container registriert. Diese Klasse ruft Benutzerinformationen von "UserDetailsService" ab und führt die Authentifizierungsverarbeitung durch.


<user-service>

Registrieren Sie "UserDetailsService" als Bean. Diese Schnittstelle bietet die Möglichkeit, detaillierte Benutzerinformationen ("UserDetails") abzurufen.

Wenn Sie dieses Tag deklarieren, wird "InMemoryUserDetailsManager" im Container registriert. Wie der Name schon sagt, enthält diese Klasse Benutzerinformationen im Speicher. Es ist sozusagen eine temporäre Implementierung für die Betriebsprüfung.


<user>

Definieren Sie eine Instanz von "UserDetails". Diese Schnittstelle definiert die Getter-Methode usw. für den Zugriff auf detaillierte Benutzerinformationen.

Durch Deklarieren dieses Tags wird eine Instanz der Klasse "User" erstellt.

Mit den Attributen "Name", "Kennwort" und "Berechtigungen" können Sie Namen, Kennwörter und Berechtigungen zur Identifizierung von Benutzern angeben.

Wenden Sie Spring Security auf Ihre Anwendung an

web.xml


    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Definieren Sie "DelegatingFilterProxy" als Servlet-Filter in "web.xml". Zu diesem Zeitpunkt wird "" mit dem Namen "springSecurityFilterChain" definiert. Jetzt wird Spring Security angewendet, wenn auf die durch definierte URL zugegriffen wird.

DelegatingFilterProxy ruft eine Bean ab, die javax.servlet.Filter aus dem Spring-Container implementiert, wobei der Name in ihrem eigenen<Filtername>festgelegt wird. Und es ist ein Servlet-Filter, der nur die Verarbeitung an diese Bean delegiert.

Hier wird "springSecurityFilterChain" für "" angegeben. Dieser Name entspricht dem FilterChainProxy-Bean-Namen , der automatisch im Container registriert wird, wenn Sie das<http>-Tag in applicationContext.xml verwenden. Ich mache es.

Mit anderen Worten, "DelegatingFilterProxy" ist für die Überbrückung des Servlet-Filters und der Spring Security ("FilterChainProxy") verantwortlich.

CSRF-Maßnahmen

index.jsp


<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Hello Spring Security!!</title>
    </head>
    <body>
        <h1>Hello Spring Security!!</h1>
        
        <c:url var="logoutUrl" value="/logout" />
        <form action="${logoutUrl}" method="post">
            <input type="submit" value="logout" />
            <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
        </form>
    </body>
</html>

Standardmäßig sind CSRF-Maßnahmen aktiviert. Daher ist es beim Senden einer Anfrage erforderlich, ein Token für CSRF-Gegenmaßnahmen gemeinsam zu übergeben.

Token-Werte und Parameternamen werden im Anforderungsbereich als "_csrf" gespeichert.

Eine detailliertere Erläuterung der CSRF-Maßnahmen wird zu einem späteren Zeitpunkt erfolgen. .. ..

Zusammenfassung

Fassen Sie zusammen, wie der Prozess hinter den Kulissen zwischen dem Starten des Servers und dem Anmelden abläuft.

Ich verwende ein Sequenzdiagramm, aber es ist nicht schlecht, da es der Klarheit halber nicht der strengen UML-Notation folgt.

Ablauf beim Serverstart

Obwohl es teilweise nicht streng ist, denke ich, dass der Initialisierungs- → Authentifizierungsprozess in der folgenden Atmosphäre durchgeführt wird.

SpringSecurity初期化時の動き.png

  1. Der Servlet-Container wird gestartet und "ContextLoaderListener", der in "web.xml" als "Listener" registriert ist, wird ausgeführt.
  2. Eine Instanz von "XmlWebApplicationContext" wird erstellt und "/ WEB-INF / applicationContext.xml" wird geladen.
  3. Das Tag registriert eine Instanz von FilterChainProxy im Spring-Container mit dem Namen springSecurityFilterChain.
  4. DelegateFilterProxy, das in web.xml als Filter registriert ist, wird vom Servlet-Container generiert.
  5. Auf Anfrage wird "DelegateFilterProxy" aufgerufen, um die Bean aus dem Spring-Container mit ihrem eigenen Namen ("springSecurityFilterChain") abzurufen ("FilterChainProxy" wird abgerufen).
  6. Die Verarbeitung wird an "FilterChainProxy" delegiert.

Flow beim Zugriff auf und Überspringen des Anmeldebildschirms, wenn nicht authentifiziert

Es ist auch nicht streng, aber ich denke, der Ablauf, wenn Sie zum Anmeldebildschirm umgeleitet werden, wenn Sie ohne Anmeldung darauf zugreifen, ist wie folgt.

ログイン画面に飛ばされるときの流れ.png

  1. FilterSecurityInterceptor ruft den Authentifizierungsprozess von AuthenticationManager auf.
  2. AuthenticationManager führt den Authentifizierungsprozess durch, löst jedoch AuthenticationException aus, da es nicht authentifiziert ist.
  3. ExceptionTranslationFilter fängt die ausgelöste Ausnahme ab.
  4. LoginUrlAuthenticationEntryPoint leitet Sie zum Anmeldebildschirm weiter.

Ablauf beim tatsächlichen Anmelden nach dem Zugriff auf den Anmeldebildschirm

Der Ablauf von unmittelbar nach der Umleitung zu "/ login" durch "LoginUrlAuthenticationEntryPoint" zur Anmeldung durch Eingabe des Benutzernamens und des Kennworts.

ログイン実行時の流れ.png

  1. DefaultLoginPageGeneratingFilter generiert den Standard-Anmeldebildschirm.
  2. Der Benutzer meldet sich an.
  3. Wenn in / login eine POST-Anfrage vorliegt, delegiert UsernamePasswordAuthenticationFilter den Authentifizierungsprozess an AuthenticationManager.
  4. "ProviderManager", eine Implementierungsklasse von "AuthenticationManager", delegiert den Authentifizierungsprozess an "AuthenticationProvider".
  5. "DaoAuthenticationProvider", eine Implementierungsklasse von "AuthenticationProvider", erfasst Benutzerinformationen ("UserDetails") von "UserDetailsService" und führt den Authentifizierungsprozess aus.
  6. Wenn die Authentifizierung erfolgreich ist, delegiert UsernamePasswordAuthenticationFilter die Verarbeitung, wenn die Authentifizierung erfolgreich ist, an AuthenticationSuccessFilter.
  7. SavedRequestAwareAuthenticationSuccessHandler, eine Implementierungsklasse von AuthenticationSuccessFilter, stellt die URL wieder her und leitet sie weiter, bevor Sie zum Anmeldebildschirm weitergeleitet wurden.

Was passiert, wenn Sie keinen Namespace verwenden?

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: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="hoge" password="HOGE" authorities="ROLE_USER" />
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>
</beans>

Der Namespace (dediziertes Tag) registriert automatisch die verschiedenen Beans, die zum Ausführen von Spring Security im Container erforderlich sind. Dank dessen sind die Spring Security-Einstellungen sehr einfach zu schreiben.

Wenn es jedoch erforderlich wird, die Authentifizierungsfunktion zu erweitern, ist es wichtig zu verstehen, welche Bohnen hinter den Kulissen registriert sind und welche Beziehung sie haben.

Um dies zu verstehen, schreiben wir die in Hello World erstellte Konfigurationsdatei ohne Verwendung des Namespace neu.

Ich werde jedoch nicht mein Bestes tun, um die Einstellung im Namespace genau anzupassen, sondern um die in Hello World angezeigten Anmeldevorgänge zu reproduzieren. Daher werden einige Bean-Definitionen weggelassen.

applicationContext.xml


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

    <bean id="springSecurityFilterChain"
          class="org.springframework.security.web.FilterChainProxy">
        <constructor-arg ref="securityFilterChain" />
    </bean>

    <bean id="securityFilterChain"
          class="org.springframework.security.web.DefaultSecurityFilterChain">
        <constructor-arg index="0">
            <bean class="org.springframework.security.web.util.matcher.AnyRequestMatcher" />
        </constructor-arg>

        <constructor-arg index="1">
            <list>
                <ref bean="securityContextPersistenceFilter" />
                <ref bean="csrfFilter" />
                <ref bean="logoutFilter" />
                <ref bean="usernamePasswordAuthenticationFilter" />
                <ref bean="defaultLoginPageGeneratingFilter" />
                <ref bean="exceptionTranslationFilter" />
                <ref bean="filterSecurityInterceptor" />
            </list>
        </constructor-arg>
    </bean>

    <!--SecurityContextHolder initialisieren / löschen-->
    <bean id="securityContextPersistenceFilter"
          class="org.springframework.security.web.context.SecurityContextPersistenceFilter" />

    <!-- CSRF -->
    <bean id="csrfFilter"
          class="org.springframework.security.web.csrf.CsrfFilter">
        <constructor-arg ref="csrfTokenRepository" />
    </bean>

    <bean id="csrfTokenRepository"
          class="org.springframework.security.web.csrf.LazyCsrfTokenRepository">
        <constructor-arg>
            <bean class="org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository" />
        </constructor-arg>
    </bean>

    <!--Ausloggen-->
    <bean id="logoutFilter"
          class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <constructor-arg index="0" value="/login?logout" />
        <constructor-arg index="1">
            <list>
                <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
                <bean class="org.springframework.security.web.csrf.CsrfLogoutHandler">
                    <constructor-arg ref="csrfTokenRepository" />
                </bean>
            </list>
        </constructor-arg>
    </bean>

    <!--Login-Authentifizierung-->
    <bean id="usernamePasswordAuthenticationFilter"
          class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
        <property name="authenticationManager" ref="authenticationManager" />
        <property name="authenticationFailureHandler">
            <bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
                <property name="defaultFailureUrl" value="/login?error" />
            </bean>
        </property>
    </bean>

    <!--Generieren Sie eine Standard-Anmeldeseite-->
    <bean id="defaultLoginPageGeneratingFilter"
          class="org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter">
        <constructor-arg ref="usernamePasswordAuthenticationFilter" />
        <property name="authenticationUrl" value="/login" />
    </bean>

    <!--Authentifizierungsausnahmehandler-->
    <bean id="exceptionTranslationFilter"
          class="org.springframework.security.web.access.ExceptionTranslationFilter">
        <constructor-arg>
            <bean class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
                <constructor-arg value="/login" />
            </bean>
        </constructor-arg>
    </bean>

    <!--Authentifizierungs- / Autorisierungsverarbeitung-->
    <bean id="filterSecurityInterceptor"
          class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="accessDecisionManager" ref="accessDecisionManager"/>
        <property name="securityMetadataSource" ref="securityMetadataSource" />
    </bean>
    
    <!--Authentifizierungsverarbeitungskörper-->
    <bean id="authenticationManager"
          class="org.springframework.security.authentication.ProviderManager">
        <constructor-arg>
            <list>
                <bean class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
                    <property name="userDetailsService">
                        <bean class="org.springframework.security.provisioning.InMemoryUserDetailsManager">
                            <constructor-arg>
                                <list>
                                    <bean class="org.springframework.security.core.userdetails.User">
                                        <constructor-arg index="0" value="hoge" />
                                        <constructor-arg index="1" value="HOGE" />
                                        <constructor-arg index="2">
                                            <bean class="org.springframework.security.core.authority.AuthorityUtils"
                                                  factory-method="commaSeparatedStringToAuthorityList">
                                                <constructor-arg value="ROLE_USER" />
                                            </bean>
                                        </constructor-arg>
                                    </bean>
                                </list>
                            </constructor-arg>
                        </bean>
                    </property>
                </bean>
            </list>
        </constructor-arg>
    </bean>

    <!--Zulassungsverarbeitungsstelle-->
    <bean id="accessDecisionManager"
          class="org.springframework.security.access.vote.AffirmativeBased">
        <constructor-arg>
            <list>
                <bean class="org.springframework.security.web.access.expression.WebExpressionVoter" />
            </list>
        </constructor-arg>
    </bean>

    <!--Definitionsinformationen wie die Zuordnung zwischen URLs, für die Authentifizierung und Berechtigungen erforderlich sind-->
    <bean id="securityMetadataSource"
          class="org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource">
        <constructor-arg index="0">
            <util:map id="requestMap" map-class="java.util.LinkedHashMap">
                <entry>
                    <key>
                        <bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
                            <constructor-arg value="/login" />
                        </bean>
                    </key>
                    <bean class="org.springframework.security.access.SecurityConfig"
                          factory-method="createList">
                        <constructor-arg value="permitAll" />
                    </bean>
                </entry>
                <entry>
                    <key>
                        <bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
                            <constructor-arg value="/**" />
                        </bean>
                    </key>
                    <bean class="org.springframework.security.access.SecurityConfig"
                          factory-method="createList">
                        <constructor-arg value="isAuthenticated()" />
                    </bean>
                </entry>
            </util:map>
        </constructor-arg>
        <constructor-arg index="1">
            <bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler" />
        </constructor-arg>
    </bean>
</beans>

Das Klassendiagramm sieht wie folgt aus.

HelloWorldの仕組み.png

Um es etwas gröber auszudrücken,

spring-security.jpg

So was.

Mit "FilterChainProxy" als Eingang werden verschiedene "Filter", die nach "SecurityFilterChain" gruppiert sind, der Reihe nach ausgeführt, und "FilterSecurityInterceptor" verarbeitet die Authentifizierung / Autorisierung unmittelbar vor dem geschützten Objekt.

Objekt sichern

Beim Lesen der Spring Security-Referenz stoße ich häufig auf den Begriff ** sicheres Objekt **.

Dies ist ein Begriff, der von Spring Security definiert wird und sich auf das bezieht, was gesichert ist (oder sein sollte).

Typische Beispiele sind "Webanforderung" und "Ausführung der Methode".

Ist FilterSecurityInterceptor ein Filter oder ein Interceptor?

Als ich zum ersten Mal den Namen "FilterSecurityInterceptor" sah, war ich verwirrt über "Ist das" Filter "oder" Interceptor "?"

Diese Klasse implementiert die Filter-Schnittstelle, sodass sie auf den ersten Blick wie Filter aussieht.

Die Antwort, oder besser gesagt, als ich mir die Typhierarchie dieser Klasse ansah, lernte ich die wahre Natur dieser Klasse kennen.

FilterSecurityIntercpetorのクラス階層.png

MethodSecurityInterceptor, dessen AbstractSecurityInterceptor` im selben übergeordneten Element enthalten ist, existiert als Geschwisterklasse.

Das heißt, der "Interceptor" für die Sicherheit von "Filter" ist "FilterSecurityInterceptor". Da "Interceptor" für die Sicherheit der Methodenausführung "MethodSecurityInterceptor" ist, handelt es sich nur um "Interceptor".

Der "FilterSecurityInterceptor" ist sozusagen "der" Interceptor "für den" Filter "durch den" Filter "des" Filters ". vielleicht.

Java Configuration Ab Spring Security 3.2 ist die in Spring 3.1 hinzugefügte Java-Konfiguration verfügbar. Mit der Java-Konfiguration können Sie Einstellungen ähnlich wie Namespaces ohne XML-Datei schreiben.

Durch die Verwendung der Java-Konfiguration kann der Compiler nach einigen Konfigurationsfehlern suchen und das Refactoring wird einfacher.

Ersetzen Sie Hello World durch Java Configuration

Implementierung

Ordnerstruktur


|-build.gradle
`-src/main/
  |-java/
  |  `-sample/spring/security/
  |    `-MySpringSecurityConfig.java
  `-webapp/
    |-index.jsp
    `-WEB-INF/
      `-web.xml

web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </context-param>
    
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>sample.spring.security.MySpringSecurityConfig</param-value>
    </context-param>
</web-app>

MySpringSecurityConfig.java


package sample.spring.security;

import org.springframework.beans.factory.annotation.Autowired;
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;

@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin();
    }

    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("hoge").password("HOGE").roles("USER");
    }
}

index.jsp bleibt unverändert.

Erläuterung

Ändern Sie die Implementierung des Containers

web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app ...>
    
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </context-param>
    
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>sample.spring.security.MySpringSecurityConfig</param-value>
    </context-param>
</web-app>

Ändern Sie die Einstellung so, dass "AnnotationConfigWebApplicationContext" anstelle von "XmlWebApplicationContext" verwendet wird, das standardmäßig verwendet wurde.

Wenn Sie "" mit dem Namen "contextClass" definieren, verwendet "ContextLoaderListener" die dort angegebene Klasse als Container.

Darüber hinaus kann die von "AnnotationConfigWebApplicationContext" gelesene Konfigurationsklasse angegeben werden, indem "" mit dem Namen "contextConfigLocation" definiert wird.

Aktivieren Sie die Federsicherheit

MySpringSecurityConfig.java


package sample.spring.security;

...
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    ...
}

Um Java Configuration for Spring Security zu aktivieren, kommentieren Sie die mit "AnnotationConfigWebApplicationContext" geladene Klasse mit "@ EnableWebSecurity".

Sie können sehen, warum Spring Security aktiviert ist, indem Sie sich die Implementierung dieser Anmerkung ansehen.

EnableWebSecurity.java


...
package org.springframework.security.config.annotation.web.configuration;

...

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;

...
@Import({ WebSecurityConfiguration.class,
		SpringWebMvcImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
...
}

WebSecurityConfiguration wird mit @ Import geladen. Diese Klasse ist auch eine Konfigurationsklasse, die mit "@ Configuration" versehen ist. </ span>

WebSecurityConfiguration.java


...
package org.springframework.security.config.annotation.web.configuration;
...

import javax.servlet.Filter;

...
@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
	...

	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		...
		return webSecurity.build();
	}

	...
}

Der Wert von "AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME" ist "springSecurityFilterChain". Der Rückgabewert von "webSecurity.build ()" ist "FilterChainProxy", und die Bean wird auf dieselbe Weise registriert wie in XML.

Kommentieren Sie also mit "@ EnableWebSecurity", um Spring Security zu aktivieren.

Spring Security-Einstellungen

MySpringSecurityConfig.java


package sample.spring.security;

...
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin();
    }

    ...
}

Um bestimmte Einstellungen für Spring Security vorzunehmen, erben Sie zunächst die Klasse "WebSecurityConfigurerAdapter".

Der "WebSecurityConfigurerAdapter" dient standardmäßig zum Erstellen einer einfachen "SecurityFilterChain". Durch Erben dieser Klasse und Überschreiben von Methoden wie "configure (HttpSecurity)" können Sie die zu erstellende "SecurityFilterChain" anpassen.

Die als Argument empfangene "HttpSecurity" entspricht dem "" - Tag im Namespace. Grundsätzlich können die mit <http> vorgenommenen Einstellungen auch mit HttpSecurity vorgenommen werden.

Die obige Implementierung hat dieselbe Bedeutung wie das folgende XML. (* Das Abmelden wird weggelassen, da "WebSecurityConfigurerAdapter" automatisch intern registriert wird.)

applicaitonContext.xml


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

Die Methode "authorizeRequests ()" ist die Methode, die die Einstellung von "" im Namespace initiiert. Der Rückgabetyp der Methode wurde durch eine Klasse namens "ExpressionInterceptUrlRegistry" ersetzt, und eine dedizierte Einstellungsmethode kann aufgerufen werden.

Wenn Sie die Einstellung von beenden und eine andere Einstellung fortsetzen möchten, fügen Sie die Methode und () ein. Die Methode and () gibt HttpSecurity zurück, sodass Sie mit der nächsten Konfiguration fortfahren können, ohne die Methodenkette zu unterbrechen.

formLogin () aktiviert, wie der Name schon sagt, die Formularauthentifizierung.

Definition von Benutzerinformationen

MySpringSecurityConfig.java


package sample.spring.security;

...
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;

@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    ...

    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("hoge").password("HOGE").roles("USER");
    }
}

"AuthenticationManagerBuilder" ist eine Unterstützungsklasse zum Definieren der folgenden Klassen von "AuthenticationManager". Sie können "UserDetailsService" usw. mithilfe der Methodenkette wie in der obigen Implementierung definieren.

Übrigens können Sie eine Methode deklarieren, die eine Bean direkt definiert, ohne "AuthenticationManagerBuilder" zu verwenden.

Bei der Einstellung in der Bean-Definition


package sample.spring.security;

import org.springframework.context.annotation.Bean;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
...

@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    ...
    
    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("hoge").password("HOGE").roles("USER").build());
        return manager;
    }
}

Initialisierungsablauf für die Java-Konfiguration

Es scheint ungefähr, dass es im folgenden Ablauf initialisiert wird.

JavaConfigの初期化.png

Verwenden Sie nicht web.xml

Wenn Ihr AP-Server Servlet 3.0 oder höher ist, können Sie Spring Security ohne web.xml verwenden.

Implementierung

Ordnerstruktur


|-build.gradle
`-src/main/
  |-java/
  |  `-sample/spring/security/
  |    |-MySpringSecurityInitializer.java
  |    `-MySpringSecurityConfig.java
  `-web/
    `-index.jsp

MySpringSecurityInitializer.java


package sample.spring.security;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class MySpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
    public MySpringSecurityInitializer() {
        super(MySpringSecurityConfig.class);
    }
}

MySpringSecurityConfig.java und index.jsp sind die gleichen wie die vorherigen, daher werde ich sie weglassen.

Erläuterung

Ich habe "web.xml" entfernt und stattdessen eine Klasse namens "MySpringSecurityInitializer" hinzugefügt.

Diese Klasse wird durch Erben von "AbstractSecurityWebApplicationInitializer" erstellt. Implementieren Sie dann den Konstruktor so, dass das Objekt "Class" der Java-Konfigurationsklasse ("MySpringSecurityConfig") an die übergeordnete Klasse übergeben wird.

Dadurch werden alle Einstellungen entfernt, die in "web.xml" vorgenommen wurden.

Wie es funktioniert

Zur Realisierung dieser Funktion werden mehrere in Servlet 3.0 hinzugefügte Mechanismen verwendet. Mal sehen, wie die Einstellung ohne "web.xml" realisiert wird.

ServletContainerInitializer AbstractSecurityWebApplicationInitializer implementiert eine Schnittstelle namens WebApplicationInitializer. Das Javadoc für diesen WebApplicationInitializer lautet:

WebApplicationInitializer.java


/**
 * ...
 *
 * <p>Implementations of this SPI will be detected automatically by {@link
 * SpringServletContainerInitializer}, which itself is bootstrapped automatically
 * by any Servlet 3.0 container. See {@linkplain SpringServletContainerInitializer its
 * Javadoc} for details on this bootstrapping mechanism.
 * ...
 */
public interface WebApplicationInitializer {

Es scheint, dass die Klasse, die diese Schnittstelle implementiert, automatisch von der Klasse "SpringServletContainerInitializer" in der Umgebung von Servlet 3.0 oder höher geladen wird.

Sehen Sie sich die Implementierung von SpringServletContainerInitializer an.

SpringServletContainerInitializer.java


package org.springframework.web;

...
import javax.servlet.ServletContainerInitializer;
import javax.servlet.annotation.HandlesTypes;

...
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
   ...

Es implementiert "ServletContainerInitializer" und ist mit "@ HandlesTypes" versehen. Beide wurden in Servlet 3.0 hinzugefügt.

Die Methode onStartup (Set <Class <? >>, ServletContext) dieser Klasse wird ausgeführt, wenn der Servlet-Container gestartet wird. Im ersten Argument der Methode wird die durch "@ HandlesTypes" angegebene Klasse [^ 1], die sich auf "Class" bezieht, automatisch vom Container durchsucht und übergeben. Im Fall von "SpringServletContainerInitializer" wird "WebApplicationInitializer" angegeben, sodass die Klasse, die diese Schnittstelle implementiert, vom Servlet-Container durchsucht und übergeben wird.

[^ 1]: Für Schnittstellen die Implementierungsklasse, für abstrakte / konkrete Klassen, die Klassen und Unterklassen und für Anmerkungen die mit Anmerkungen versehene Klasse.

Wenn die Methode "onStartup ()" des "SpringServletContainerInitializer" ausgeführt wird, wird eine Instanz der Klasse erstellt, die den "WebApplicationInitializer" implementiert, und die Methode "onStartup (ServletContext)" des "WebApplicationInitializer" wird ausgeführt.

AbstractSecurityWebApplicationInitializer Die Methode "onStartup (ServletContext)" von "AbstractSecurityWebApplicationInitializer" hat die folgende Implementierung.

AbstractSecurityWebApplicationInitializer.java


package org.springframework.security.web.context;

import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set;

import javax.servlet.Filter;
import javax.servlet.ServletContext;
...

import org.springframework.context.ApplicationContext;
...
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.AbstractContextLoaderInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.DelegatingFilterProxy;

...
public abstract class AbstractSecurityWebApplicationInitializer
		implements WebApplicationInitializer {

	...

	public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";

	...

	public final void onStartup(ServletContext servletContext) throws ServletException {
		beforeSpringSecurityFilterChain(servletContext);
		if (this.configurationClasses != null) {
			AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext(); ★
			rootAppContext.register(this.configurationClasses);
			servletContext.addListener(new ContextLoaderListener(rootAppContext)); ★
		}
		if (enableHttpSessionEventPublisher()) {
			servletContext.addListener(
					"org.springframework.security.web.session.HttpSessionEventPublisher");
		}
		servletContext.setSessionTrackingModes(getSessionTrackingModes());
		insertSpringSecurityFilterChain(servletContext);
		afterSpringSecurityFilterChain(servletContext);
	}

	...

	private void insertSpringSecurityFilterChain(ServletContext servletContext) {
		String filterName = DEFAULT_FILTER_NAME;
		DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy(
				filterName); ★
		String contextAttribute = getWebApplicationContextAttribute();
		if (contextAttribute != null) {
			springSecurityFilterChain.setContextAttribute(contextAttribute);
		}
		registerFilter(servletContext, true, filterName, springSecurityFilterChain);
	}

        ...
}

Hier und da gibt es einige bekannte Klassen ...

Kurz gesagt, die Einstellungen, die in "web.xml" vorgenommen wurden, werden in dieser Implementierung vorgenommen.

Zusammenfassung

Es wird durch den folgenden Ablauf initialisiert. (Wie üblich gibt es einige Teile, die zur Vereinfachung nicht streng sind)

Servlet3.0での初期化の流れ.png

Referenz

Recommended Posts

Spring Security-Nutzungsnotiz Basic / Mechanismus
Spring Security-Nutzungsnotiz CSRF
Spring Security-Nutzungsnotiz Run-As
Sicherheit der Verwendungsnotizmethode für Spring Security
Spring Security-Nutzungsnotiz Remember-Me
Spring Security-Nutzungsnotiz CORS
Spring Security-Verwendungsnotiztest
Sitzungsverwaltung für Spring Security-Nutzungsnotizen
Spring Security Usage Memo Domänenobjektsicherheit (ACL)
Verwendungshinweis zu Spring Security: Zusammenarbeit mit Spring MVC und Boot
Erreichen Sie die BASIC-Authentifizierung mit Spring Boot + Spring Security
Frühlingsrückblick Memo
Verwendungshinweise zu JavaParser
Hinweise zur Verwendung von WatchService
PlantUML-Nutzungsnotiz
Verwendungshinweise zu JUnit5
Informationen zur Spring Security-Authentifizierung
Java Grundwissen Memo
Ruby grundlegende Syntaxnotizen
Spring Security erhöht 403 verboten
Java-Lernnotiz (grundlegend)
Schreiben von Spring Boot-Memos (2)
[Persönliche Notizen] Über das Spring Framework
JJUG CCC 2018 Frühlingsbeteiligungsprotokoll
Spring Framework Selbststudium Memo series_1
Grundlegende Verwendungshinweise für Jackson
schnelle CollectionView Super grundlegende Verwendung
[Java] Thymeleaf Basic (Spring Boot)
Anmeldefunktion mit Spring Security
[Frühlingssicherheit] Frühlingssicherheit auf GAE (SE)
Hinweise zur Verwendung des Abhängigkeitsmanagement-Plugins
Versuchen Sie es mit Spring Boot Security
Memo zur Spring Boot Controller-Methode