[JAVA] Mémo d'utilisation de Spring Security Basic / mécanisme

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

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

environnement

Java 1.8.0_xxx

Serveur AP

Tomcat 8.0.35

Gradle 3.2.1

OS Windows 10

Qu'est-ce que Spring Security?

Un des projets de Spring. Un cadre qui ajoute principalement des fonctionnalités de sécurité aux applications Web.

Il offre une protection contre une variété d'attaques bien connues telles que CSRF et les attaques de fixation de session.

Hello World Ici est écrit dans un peu plus d'ordre pour l'annonce. Cela peut être plus facile à comprendre si vous le lisez avec la diapositive (je pense qu'elle n'a été écrite que sur la diapositive).

la mise en oeuvre

Structure des dossiers


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

Contrôle de fonctionnement

Générez un fichier war (spring-security-sample.war) avec gradle war et déployez-le sur Tomcat.

Une fois le déploiement terminé, ouvrez http: // localhost: 8080 / spring-security-sample dans votre navigateur.

spring-security.jpg

Un mystérieux écran de connexion s'affiche.

Saisissez respectivement «hoge» et «HOGE» dans User et Password et cliquez sur le bouton «Login».

spring-security.jpg

ʻLe contenu de index.jsp` est affiché.

Enfin, cliquez sur le bouton déconnexion.

spring-security.jpg

La déconnexion est terminée et vous revenez à l'écran de connexion.

La description

Dépendances

build.gradle


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

Dans la configuration minimale, vous pouvez ajouter spring-security-web et spring-security-config comme dépendances cela semble bon .RELEASE / reference / htmlsingle / #gradle).

spring-security-web

Il contient du code lié à "Filtre" et aux applications Web. Si vous avez besoin d'une authentification Web ou d'un contrôle d'accès basé sur les URL, vous avez besoin de ce module. Le paquet principal est ʻorg.springframework.security.web`

spring-security-config

Il contient du code pour analyser l'espace de noms utilisé pour écrire des définitions en xml et du code pour la configuration Java. Ce module est requis pour la configuration XML et les configurations Java. Le paquet principal est ʻorg.springframework.security.config` Les classes incluses ici ne sont pas utilisées directement par l'application.

Initialisation du conteneur Spring

web.xml


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

En enregistrant ContextLoaderListener comme écouteur, le conteneur Spring (ʻApplicationContext`) est initialisé.

ʻSi vous ne spécifiez pas ce qu'il faut utiliser pour la classe ApplicationContext, XmlWebApplicationContext est utilisé par défaut. Il est défini dans ʻorg / springframework / web / context / ContextLoader.properties de spring-web-x.x.x.RELEASE.jar comme suit, et ContextLoaderListener est chargé lorsque le conteneur de servlet est initialisé.

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 charge WEB-INF / applicationContext.xml comme fichier de configuration par défaut.

Paramètres de sécurité Spring

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` lui-même est un fichier de configuration standard pour la définition de Spring Bean, pas un fichier de configuration spécial pour Spring Security.

En chargeant http: // www.springframework.org / schema / security avec xmlns, vous pouvez utiliser la balise pour Spring Security (dans la référence, utilisez cette balise dédiée à Spring Security * Nous l'appelons * namespace **). Vous pouvez utiliser cette balise pour configurer les paramètres de Spring Security sur ce fichier.

Les paramètres ci-dessus définissent approximativement les éléments suivants:

Les étiquettes individuelles seront décrites un peu plus en détail ci-dessous.


<http>

Lorsque vous définissez la balise <http>, certains beans sont automatiquement enregistrés dans le conteneur. Parmi eux, les deux classes suivantes sont des beans importants.

  1. FilterChainProxy
  2. SecurityFilterChain

FilterChainProxy est enregistré dans le conteneur avec le nom du bean "springSecurityFilterChain". </ span> Cette classe est la passerelle vers le traitement Spring Security.

SecurityFilterChain est juste une interface en soi, et DefaultSecurityFilterChain est enregistré dans le conteneur en tant que classe d'implémentation. Comme son nom l'indique, «SecurityFilterChain» est une chaîne de «javax.servlet.Filter» qui a une fonction de sécurité et contient plusieurs «Filter». Spring Security réalise la fonction de sécurité en utilisant Filter.

Comme vous pouvez le voir à partir du nom FilterChainProxy, la classe elle-même ne fait rien. Un traitement spécifique est délégué aux Filters de la SecurityFilterChain.


<intercept-url>

Les conditions (contrôle d'accès) requises pour l'accès sont définies pour chaque modèle d'URL.

L'attribut pattern peut être décrit dans ant path format.

Dans l'attribut ʻaccess, spécifiez les conditions requises pour accéder à l'URL spécifiée dans l'attribut pattern. «permitAll» signifie autoriser tous les accès (aucune authentification requise). Et ʻisAuthenticated () signifie autoriser l'accès s'il est authentifié (connecté).

L'attribut ʻaccess` est décrit en utilisant le propre langage d'expression de Spring appelé Spring Expression Language (SpEL).


<form-login>

Définit que l'authentification par formulaire est requise.

En cas d'erreur d'authentification, vous serez redirigé vers / login par défaut. Par défaut, accéder à / login vous amènera à une simple page de connexion fournie par Spring Security.

Cette page de connexion par défaut est générée par DefaultLoginPageGeneratingFilter dans spring-security-web-x.x.x.RELEASE.jar.


<logout>

En ajoutant cette balise, vous pourrez vous déconnecter.

Par défaut, une requête POST à / logout entraînera une déconnexion.


<authentication-manager>

ʻDéfinir AuthenticationManager comme un bean. ʻAuthenticationManager est une classe qui exécute le traitement d'authentification et doit être définie.

ʻAuthenticationManager lui-même est une interface, et la classe d'implémentation utilise ProviderManager. ProviderManager lui-même n'effectue pas de traitement d'authentification spécifique, mais délègue le traitement d'authentification à ʻAuthenticationProvider, qui sera décrit plus loin.


<authentication-provider>

ʻDefine AutheticationProvider` comme un bean. Cette interface fournit un traitement d'authentification spécifique en fonction du type d'authentification.

Par exemple, la classe qui fournit le traitement d'authentification LDAP est comme LdapAuthenticationProvider.

Si vous déclarez cette balise, DaoAuthenticationProvider est enregistré dans le conteneur. Cette classe obtient les informations utilisateur de ʻUserDetailsService` et effectue le traitement d'authentification.


<user-service>

ʻEnregistrez UserDetailsService comme un bean. Cette interface offre la possibilité de récupérer des informations détaillées sur l'utilisateur (ʻUserDetails).

La déclaration de cette balise enregistrera ʻInMemoryUserDetailsManager` dans le conteneur. Comme son nom l'indique, cette classe contient les informations utilisateur en mémoire. Pour ainsi dire, il s'agit d'une mise en œuvre temporaire pour le contrôle de fonctionnement.


<user>

ʻDéfinir une instance de UserDetails`. Cette interface définit la méthode Getter, etc. pour accéder aux informations utilisateur détaillées.

La déclaration de cette balise crée une instance de la classe ʻUser`.

Les attributs «nom», «mot de passe», «autorités» vous permettent de spécifier des noms, des mots de passe et des autorisations pour identifier les utilisateurs.

Appliquez Spring Security à votre application

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>

Définissez DelegatingFilterProxy comme filtre de servlet dans web.xml. A ce moment, <nom-filtre> est défini avec le nom " springSecurityFilterChain ". Désormais, Spring Security sera appliqué lors de l'accès à l'URL définie par ` ''.

DelegatingFilterProxy obtient un bean qui implémente javax.servlet.Filter à partir du conteneur Spring en utilisant le nom défini dans son propre<nom-filtre>. Et c'est un filtre de servlet qui délègue simplement le traitement à ce bean.

Ici, «springSecurityFilterChain» est spécifié pour «». Ce nom est un avec le nom du bean FilterChainProxy qui est automatiquement enregistré dans le conteneur lorsque vous utilisez la balise <http> dans ʻapplicationContext.xml`. Je le fais.

En d'autres termes, DelegatingFilterProxy est responsable du pontage du filtre de servlet et de Spring Security ( FilterChainProxy).

Mesures CSRF

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>

Par défaut, les mesures CSRF sont activées. Par conséquent, lors de l'envoi d'une demande, il est nécessaire de transmettre un jeton pour les contre-mesures CSRF ensemble.

Les valeurs de jeton et les noms de paramètres sont stockés dans la portée de la requête en tant que _csrf.

Une explication plus détaillée des mesures du CSRF sera fournie ultérieurement. .. ..

Résumé

Résumez la façon dont le processus se déroule dans les coulisses entre le démarrage du serveur et la connexion.

J'utilise un diagramme de séquence, mais ce n'est pas mal car il ne suit pas la notation UML stricte pour plus de clarté.

Flux au démarrage du serveur

Bien que ce ne soit pas en partie strict, je pense que le processus d'initialisation → d'authentification est effectué dans l'atmosphère suivante.

SpringSecurity初期化時の動き.png

  1. Le conteneur de servlet démarre et ContextLoaderListener, qui est enregistré comme Listener dans web.xml, est exécuté.
  2. Une instance de XmlWebApplicationContext est créée et / WEB-INF / applicationContext.xml est chargé.
  3. La balise <http> enregistre une instance de FilterChainProxy avec le conteneur Spring avec le nom" "springSecurityFilterChain" .
  4. DelegateFilterProxy, qui est enregistré comme Filter dans web.xml, est généré par le conteneur de servlet.
  5. À la demande, DelegateFilterProxy est appelé pour récupérer le bean du conteneur Spring avec son propre nom ("springSecurityFilterChain") ( FilterChainProxy est obtenu).
  6. Le traitement est délégué à FilterChainProxy.

Débit lors de l'accès sans authentification et passage à l'écran de connexion

Ce n'est pas non plus strict, mais je pense que le flux lorsque vous êtes redirigé vers l'écran de connexion lorsque vous y accédez sans vous connecter est le suivant.

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

  1. FilterSecurityInterceptor appelle le processus d'authentification de ʻAuthenticationManager`.
  2. ʻAuthenticationManager exécute le processus d'authentification, mais lance ʻAuthenticationException parce qu'il n'est pas authentifié.
  3. ʻExceptionTranslationFilter` intercepte l'exception levée.
  4. LoginUrlAuthenticationEntryPoint vous redirige vers l'écran de connexion.

Flux lors de la connexion après avoir accédé à l'écran de connexion

Le flux entre immédiatement après avoir été redirigé vers / login par LoginUrlAuthenticationEntryPoint pour se connecter en entrant le nom d'utilisateur et le mot de passe.

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

  1. DefaultLoginPageGeneratingFilter générera l'écran de connexion par défaut.
  2. L'utilisateur se connecte.
  3. Lorsqu'il y a une requête POST dans / login, ʻUsernamePasswordAuthenticationFilter délègue le processus d'authentification à ʻAuthenticationManager.
  4. ProviderManager, qui est une classe d'implémentation de ʻAuthenticationManager, délègue le processus d'authentification à ʻAuthenticationProvider.
  5. DaoAuthenticationProvider, qui est une classe d'implémentation de ʻAuthenticationProvider, acquiert les informations utilisateur (ʻUserDetails) de ʻUserDetailsService` et exécute le processus d'authentification.
  6. Si l'authentification réussit, ʻUsernamePasswordAuthenticationFilter délègue le traitement de l'authentification réussie à ʻAuthenticationSuccessFilter.
  7. Classe d'implémentation ʻAuthenticationSuccessFilter`` SavedRequestAwareAuthenticationSuccessHandler` restaure et redirige l'URL à laquelle vous tentiez d'accéder avant d'être redirigé vers l'écran de connexion.

Que se passe-t-il si vous n'utilisez pas d'espace de noms

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>

L'espace de noms (balise dédiée) enregistre automatiquement les différents beans nécessaires pour exécuter Spring Security dans le conteneur. Grâce à cela, les paramètres de Spring Security sont très simples à écrire.

Cependant, lorsqu'il devient nécessaire d'étendre la fonction d'authentification, il devient important de comprendre quels types de beans sont enregistrés dans les coulisses et quel type de relation ils entretiennent.

Pour comprendre cela, réécrivons le fichier de configuration créé dans Hello World sans utiliser d'espace de noms.

Cependant, je ne ferai pas de mon mieux pour que ce soit exactement le même que le paramètre dans l'espace de noms, mais pour reproduire la série de processus de connexion vu dans Hello World. Par conséquent, certaines définitions de bean sont omises.

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>

    <!--Initialiser / effacer SecurityContextHolder-->
    <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>

    <!--Se déconnecter-->
    <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>

    <!--Authentification de connexion-->
    <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>

    <!--Générer la page de connexion par défaut-->
    <bean id="defaultLoginPageGeneratingFilter"
          class="org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter">
        <constructor-arg ref="usernamePasswordAuthenticationFilter" />
        <property name="authenticationUrl" value="/login" />
    </bean>

    <!--Gestionnaire d'exceptions d'authentification-->
    <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>

    <!--Traitement d'authentification / d'autorisation-->
    <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>
    
    <!--Corps de traitement d'authentification-->
    <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>

    <!--Organisme de traitement des autorisations-->
    <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>

    <!--Informations de définition telles que le mappage entre les URL qui nécessitent une authentification et des autorisations-->
    <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>

Le diagramme de classes ressemble à ce qui suit.

HelloWorldの仕組み.png

Pour le dire un peu plus grossièrement,

spring-security.jpg

Comme ça.

Avec FilterChainProxy comme entrée, divers Filters regroupés par SecurityFilterChain sont exécutés dans l'ordre, et FilterSecurityInterceptor traite l'authentification / autorisation juste avant l'objet protégé.

Objet sécurisé

En lisant la référence Spring Security, je rencontre souvent le terme ** objet sécurisé **.

Il s'agit d'un terme défini par Spring Security et fait référence à ce qui est (ou devrait être) sécurisé.

Des exemples typiques sont "requête Web" et "exécution de méthode".

FilterSecurityInterceptor est-il un filtre ou un intercepteur?

Quand j'ai vu pour la première fois le nom de «FilterSecurityInterceptor», j'étais confus quant à savoir si c'était «Filter» ou «Interceptor».

Cette classe implémente l'interface Filter, donc cela ressemble à Filter à première vue.

La réponse, ou plutôt, quand j'ai regardé la hiérarchie des types de cette classe, j'ai appris à connaître la vraie nature de cette classe.

FilterSecurityIntercpetorのクラス階層.png

ʻMethodSecurityInterceptor, qui a AbstractSecurityInterceptor comme même parent, existe en tant que classe sœur.

Autrement dit, ʻInterceptor pour la sécurité de FilterestFilterSecurityInterceptor, Puisque ʻInterceptor pour la sécurité de l'exécution de la méthode est MethodSecurityInterceptor, ce ne sont que ʻInterceptor`.

FilterSecurityInterceptor est, pour ainsi dire," ʻInterceptor pour FilterbyFilter of Filter`". peut être.

Java Configuration À partir de Spring Security 3.2, la configuration Java ajoutée dans Spring 3.1 est disponible. La configuration Java vous permet d'écrire des paramètres similaires aux espaces de noms sans fichier XML.

En utilisant la configuration Java, il y a des avantages que le compilateur vérifie certaines erreurs de configuration et que la refactorisation sera plus facile.

Remplacez Hello World par la configuration Java

la mise en oeuvre

Structure des dossiers


|-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` n'a pas changé.

La description

Changer l'implémentation du conteneur

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>

Modifiez le paramètre pour utiliser ʻAnnotationConfigWebApplicationContext au lieu de XmlWebApplicationContext`, qui était utilisé par défaut.

Si vous définissez <context-param> avec le nom contextClass, ContextLoaderListener utilisera la classe spécifiée ici comme conteneur.

De plus, la classe de configuration lue par ʻAnnotationConfigWebApplicationContext peut être spécifiée en définissant avec le nom contextConfigLocation`.

Activer Spring Security

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 {

    ...
}

Pour activer la configuration Java pour Spring Security, annotez la classe chargée avec ʻAnnotationConfigWebApplicationContext avec @ EnableWebSecurity`.

Vous pouvez voir pourquoi Spring Security est activé en examinant l'implémentation de cette annotation.

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 est chargé en utilisant @ Import. Cette classe est également une classe de configuration annotée avec @ Configuration. </ 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();
	}

	...
}

ʻLa valeur de AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME est "springSecurityFilterChain" . La valeur de retour de webSecurity.build ()estFilterChainProxy`, et le bean est enregistré de la même manière qu'il a été fait en xml.

Alors, annotez avec @ EnableWebSecurity pour activer Spring Security.

Paramètres de sécurité Spring

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

    ...
}

Pour définir des paramètres spécifiques pour Spring Security, héritez d'abord de la classe WebSecurityConfigurerAdapter.

Le WebSecurityConfigurerAdapter est conçu pour construire un SecurityFilterChain simple par défaut. En héritant de cette classe et en remplaçant des méthodes telles que configure (HttpSecurity), vous pouvez personnaliser la SecurityFilterChain à construire.

Le HttpSecurity reçu comme argument correspond à la balise <http> dans l'espace de noms. Fondamentalement, les réglages effectués avec <http> peuvent également être effectués avec HttpSecurity.

L'implémentation ci-dessus a la même signification que le XML ci-dessous. (* La déconnexion est omise car WebSecurityConfigurerAdapter est automatiquement enregistré en interne.)

applicaitonContext.xml


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

ʻLa méthode authorizeRequests () est la méthode qui lance le paramétrage de dans l'espace de noms. Le type de retour de la méthode a été remplacé par une classe appelée ʻExpressionInterceptUrlRegistry, et une méthode de paramétrage dédiée peut être appelée.

Si vous voulez terminer le réglage de <intercept-url> et continuer un autre réglage, insérez la méthode ʻand () . La méthode ʻand () renvoie HttpSecurity, vous pouvez donc continuer avec la configuration suivante sans rompre la chaîne de méthodes.

formLogin (), comme son nom l'indique, active l'authentification par formulaire.

Définition des informations utilisateur

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 est une classe de support pour définir les classes suivantes, ʻAuthenticationManager, et vous pouvez définir ʻUserDetailsService` etc. en utilisant une chaîne de méthodes comme dans l'implémentation ci-dessus.

En passant, vous pouvez déclarer une méthode qui définit un bean directement sans utiliser ʻAuthenticationManagerBuilder`.

Lors du réglage dans la définition du bean


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

Flux d'initialisation de la configuration Java

En gros, il semble qu'il soit initialisé dans le flux suivant.

JavaConfigの初期化.png

N'utilisez pas web.xml

Si votre serveur AP est Servlet 3.0 ou supérieur, vous pouvez commencer à utiliser Spring Security sans web.xml.

la mise en oeuvre

Structure des dossiers


|-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 et ʻindex.jsp` sont les mêmes que les précédents, je vais donc les omettre.

La description

J'ai supprimé web.xml et ajouté une classe appelée MySpringSecurityInitializer à la place.

Cette classe est créée en héritant de ʻAbstractSecurityWebApplicationInitializer. Ensuite, implémentez le constructeur pour que l'objet Class de la classe Java Configuration ( MySpringSecurityConfig`) soit passé à la classe parent.

Cela élimine tous les paramètres définis dans web.xml.

Comment ça fonctionne

Plusieurs mécanismes ajoutés dans Servlet 3.0 sont utilisés pour réaliser cette fonction. Voyons comment le paramètre est réalisé sans web.xml.

ServletContainerInitializer ʻAbstractSecurityWebApplicationInitializer implémente une interface appelée WebApplicationInitializer. Le Javadoc pour ce WebApplicationInitializer` indique:

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 {

Il semble que la classe qui implémente cette interface est automatiquement chargée par la classe SpringServletContainerInitializer dans l'environnement de Servlet 3.0 ou supérieur.

Allez voir l'implémentation de SpringServletContainerInitializer.

SpringServletContainerInitializer.java


package org.springframework.web;

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

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

Il implémente ServletContainerInitializer et est annoté avec @ HandlesTypes. Les deux ont été ajoutés dans Servlet 3.0.

La méthode ʻonStartup (Set <Class <? >>, ServletContext) de cette classe est exécutée au démarrage du conteneur Servlet. Dans le premier argument de la méthode, la classe [^ 1] liée à Class spécifiée par @ HandlesTypesest automatiquement recherchée par le conteneur et transmise. Dans le cas deSpringServletContainerInitializer, WebApplicationInitializer` est spécifié, donc la classe qui implémente cette interface est recherchée par le conteneur Servlet et transmise.

[^ 1]: Pour les interfaces, la classe d'implémentation, pour les classes abstraites / concrètes, les classes et sous-classes, et pour les annotations, la classe annotée.

Lorsque la méthode ʻonStartup () de SpringServletContainerInitializer est exécutée, une instance de la classe qui implémente WebApplicationInitializerest créée et la méthode ʻonStartup (ServletContext)de WebApplicationInitializer est exécutée.

AbstractSecurityWebApplicationInitializer La méthode ʻonStartup (ServletContext) de ʻAbstractSecurityWebApplicationInitializer a l'implémentation suivante.

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

        ...
}

Il y a des cours familiers ici et là ...

En bref, les paramètres définis dans web.xml sont définis dans cette implémentation.

Résumé

Il est initialisé par le flux suivant. (Comme d'habitude, certaines parties ne sont pas strictes pour simplifier)

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

référence

Recommended Posts

Mémo d'utilisation de Spring Security Basic / mécanisme
Mémo d'utilisation de Spring Security CSRF
Mémo d'utilisation de Spring Security Run-As
Spring Security Usage Memo Method Security
Mémo d'utilisation de Spring Security Remember-Me
Mémo d'utilisation de Spring Security CORS
Test de mémo d'utilisation de Spring Security
Gestion des sessions de mémo d'utilisation de Spring Security
Spring Security Usage Memo Domain Object Security (ACL)
Mémo d'utilisation de Spring Security: coopération avec Spring MVC et Boot
Obtenez une authentification BASIC avec Spring Boot + Spring Security
Mémo rétrospective du printemps
Notes d'utilisation de JavaParser
Notes d'utilisation de WatchService
Mémo d'utilisation PlantUML
Notes d'utilisation de JUnit5
À propos de l'authentification Spring Security
mémo de connaissances de base java
notes de syntaxe de base ruby
Spring Security soulève 403 interdits
Mémo d'apprentissage Java (basique)
Rédaction de mémos de démarrage de printemps (2)
[Notes personnelles] À propos du framework Spring
Mémo de participation au printemps JJUG CCC 2018
Série de mémos d'auto-apprentissage Spring Framework_1
Notes d'utilisation de base pour Jackson
swift CollectionView Utilisation super basique
[Java] Thymeleaf Basic (Spring Boot)
Fonction de connexion avec Spring Security
[Spring Security] Spring Security sur GAE (SE)
Notes sur l'utilisation du plug-in de gestion des dépendances
Essayez d'utiliser Spring Boot Security
Mémo de méthode de contrôleur de démarrage à ressort