Sécurité avec acegi

Cette page a été rédigée il y a fort fort longtemps, et n'a pas tellement été mise à jour.

 

Vous savez, moi je ne crois pas qu'il y ait de bonne ou de mauvaise page. Moi, si je devais résumer mon wiki aujourd'hui avec vous, je dirais que c'est d'abord des rencontres. Des gens qui m'ont tendu la main, peut-être à un moment où je ne pouvais pas, où j'étais seul chez moi. Et c'est assez curieux de se dire que les hasards, les rencontres forgent une destinée... Parce que quand on a le goût de la chose, quand on a le goût de la chose bien faite, le beau geste, parfois on ne trouve pas l'interlocuteur en face je dirais, le miroir qui vous aide à avancer. Alors ça n'est pas mon cas, comme je disais là, puisque moi au contraire, j'ai pu ; et je dis merci au wiki, je lui dis merci, je chante le wiki, je danse le wiki... je ne suis qu'amour ! Et finalement, quand des gens me disent « Mais comment fais-tu pour avoir cette humanité ? », je leur réponds très simplement que c'est ce goût de l'amour, ce goût donc qui m'a poussé aujourd'hui à entreprendre une construction logicielle... mais demain qui sait ? Peut-être simplement à me mettre au service de la communauté, à faire le don, le don de soi.

Paramétrage général

Le filtre principal se paramètre dans le fichier web.xml.

<filter>
  <filter-name>Acegi</filter-name>
    <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
    <init-param>
      <param-name>targetClass</param-name>
      <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>Acegi</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</filter>

Dans un fichier de contexte Spring (securityContext.xml, par exemple), on paramètre la chaîne de filtres.

 <bean id="filterChainProxy"
   class="org.acegisecurity.util.FilterChainProxy">
   <property name="filterInvocationDefinitionSource">
     <value>
       CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
       PATTERN_TYPE_APACHE_ANT
       /**=aFilter1,aFilter2,aFilter3
     </value>
   </property>
 </bean>

Les filtres présents dans la liste seront appelés dans l’ordre de déclaration.

L’enchaînement classique de filtre est le suivant :

  • httpSessionContextIntegrationFilter : permet de stocker les informations d’authentification en session

  • xxxProcessingFilter : impose une technique d’authentification (BASIC, FORM,…​)

  • securityContextHolderAwareRequestFilter : permet de stocker les informations d’authentification dans la requête, ce qui permet d’utiliser l’API standard avec request.getUserPrincipal() et request.isUserInRole().

  • exceptionTranslationFilter : permet de gérer les erreurs de sécurité, comme une tentative d’accès avant le login

  • filterInvocationInterceptor : gère les autorisations

Authentification Web BASIC

httpSessionContextIntegrationFilter

La déclaration de ce filtre est simple et classique :

 <bean id="httpSessionContextIntegrationFilter"
   class="org.acegisecurity.context.HttpSessionContextIntegrationFilter" />

Ce filtre est nécessaire dans les applications Web, pour éviter à l’utilisateur de saisir son login à chaque requête.

basicProcessingFilter

Le xxxProcessingFilter prend en charge l’authentification standard http « BASIC ». Le login et le mot de passe sont saisis dans une boite de dialogue du navigateur. Celui-ci se charge de transmettre les informations dans la requête. Les informations sont habituellement gardées en cache dans le navigateur.

 <bean id="basicProcessingFilter"
   class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
   <property name="authenticationManager">
     <ref local="authenticationManager" />
   </property>
   <property name="authenticationEntryPoint">
     <bean id="basicProcessingFilterEntryPoint"
       class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
       <property name="realmName">
         <value>Calcul Realm</value>
       </property>
     </bean>
   </property>
 </bean>

Les propriétés à déclarer sont l’authenticationManager, qui indique la technique de stockage du login et du password, et l’authenticationEntryPoint, qui spécifie la technique d’authentification.

L’authenticationManager liste des providers, capable de lui fournir des couples user/password. Dans cet exemple, on utilise un provider simple qui stocke les couples dans un fichier properties.

 <bean id="authenticationManager"
   class="org.acegisecurity.providers.ProviderManager">
   <property name="providers">
     <list>
       <bean
         class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
         <property name="userDetailsService">
           <bean id="userDetailsService"
             class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
             <property name="userProperties">
               <bean
                 class=
             "org.springframework.beans.factory.config.PropertiesFactoryBean">
                 <property name="location"
                   value="/WEB-INF/users.properties" />
               </bean>
             </property>
           </bean>
         </property>
       </bean>
     </list>
   </property>
 </bean>

Le fichier properties a le format suivant :

alexis=hassler,ROLE_SUPERVISOR
scott=tiger,ROLE_USER

securityContextHolderAwareRequestFilter

Ce filtre stocke les informations de sécurité de spring dans la request. Pour ce faire, il utilise la technique des wrappers.

 <bean id="securityContextHolderAwareRequestFilter"
   class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter"/>

exceptionTranslationFilter

Ce filtre gère les exceptions, en particulier les erreurs de type « accès refusé ». En cas d’exception de ce type, la requête est orientée vers un authenticationEntryPoint (cf. xxxProcessingFilter).

 <bean id="exceptionTranslationFilter"
   class="org.acegisecurity.ui.ExceptionTranslationFilter">
   <property name="authenticationEntryPoint">
     <bean
       class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
       <property name="realmName">
         <value>Calcul Realm</value>
       </property>
     </bean>
   </property>
   <property name="accessDeniedHandler">
     <bean
       class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
       <property name="errorPage" value="/accessDenied.jsp" />
     </bean>
   </property>
 </bean>

filterInvocationInterceptor

Ce dernier filtre gère les autorisations. La propriété « authenticationManager » indique la technique de stockage des login et des rôles ; il peut être identique à celui du xxxProcessingFilter.

   <property name="authenticationManager"
     ref="authenticationManager" />

La propriété « accessDecisionManager » permet de définir une liste de « voters ». Chacun d’entre eux accepte ou refuse l’accès ; la décision globale peut être accordée soit si 1 voter a accepté, soit à la majorité, soit à l’unanimité.

   <property name="accessDecisionManager">
     <bean class="org.acegisecurity.vote.AffirmativeBased">
       <property name="allowIfAllAbstainDecisions"
         value="false" />
       <property name="decisionVoters">
         <list>
           <bean class="org.acegisecurity.vote.RoleVoter" />
         </list>
       </property>
     </bean>
   </property>

Enfin, la 3° propriété définit les rôles permettant d’accéder aux URLs.

   <property name="objectDefinitionSource">
     <value>
       CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
       PATTERN_TYPE_APACHE_ANT
       /admin/=ROLE_ADMIN
       /*=ROLE_USER
     </value>
   </property>

Authentification Anonyme

L’authentification anonyme permet d’autoriser certaines ressources aux utilisateurs non authentifiés explicitement. Il faut d’abord déclarer un filtre d’authentification anonyme qui crée l’utilisateur « anonymous », avec le rôle « ROLE_ANONYMOUS », ainsi qu’un anonymousAuthenticationProvider qui fait référence à la clé du filtre.

<bean id="anonymousProcessingFilter"
      class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
  <property name="key">
    <value>ano</value>
  </property>
  <property name="userAttribute">
    <value>anonymousUser,ROLE_ANONYMOUS</value>
  </property>
</bean>

<bean id="anonymousAuthenticationProvider"
      class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
  <property name="key">
    <value>ano</value>
  </property>
</bean>

Enfin, les ressources autorisées aux utilisateurs anonymes sont associées au rôle ROLE_ANONYMOUS dans le filterInvocationInterceptor.

<bean id="filterInvocationInterceptor"
      class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
  <property name="authenticationManager">
    <ref bean="authenticationManager"/>
  </property>
  <property name="accessDecisionManager">
    <ref local="accessDecisionManager"/>
  </property>
  <property name="objectDefinitionSource">
    <value>
      CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
      PATTERN_TYPE_APACHE_ANT
      /index.jsp=ROLE_ANONYMOUS,ROLE_USER
      /acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER
      /**=ROLE_USER
    </value>
  </property>
</bean>