web-dev-qa-db-fra.com

Printemps 3 Sécurité: AccessDeniedHandler n'est pas appelé

J'ai une application Spring 3 avec les configurations indiquées ci-dessous. Lorsqu'un utilisateur tente d'accéder à une page et qu'il/elle n'est pas connecté (e), je reçois un accès refusé exception avec une trace de pile laide. Comment puis-je gérer cette exception et ne pas le laisser vider une trace de pile. J'ai implémenté mon propre gestionnaire d'accès avec accès refusé, mais celui-ci n'est pas appelé. 

En fonction du type de la ressource demandée, j'aimerais afficher des pages ou des messages d'erreur personnalisés. Voici ma configuration de printemps. 

Comment faire en sorte que Spring appelle mon gestionnaire d'accès avec accès refusé. Voici ma configuration de printemps

 <security:http auto-config='true'>
    <security:intercept-url pattern="/static/**" filters="none"/>
    <security:intercept-url pattern="/login" filters="none"/>

      <security:intercept-url pattern="/**" access="ROLE_USER" />

      <security:form-login login-page="/index"
            default-target-url="/home" always-use-default-target="true"
            authentication-success-handler-ref="AuthenticationSuccessHandler"        
            login-processing-url="/j_spring_security_check" 
            authentication-failure-url="/index?error=true"/>

       <security:remember-me key="myLongSecretCookieKey" token-validity-seconds="1296000" 
            data-source-ref="jdbcDataSource" user-service-ref="AppUserDetailsService" />

       <security:access-denied-handler ref="myAccessDeniedHandler" />   

    </security:http>

    <bean id="myAccessDeniedHandler"
         class="web.exceptions.handlers.AccessDeniedExceptionHandler">
      <property name="errorPage" value="/public/403.htm" />
    </bean>

La classe personnalisée pour gérer cette exception est donnée ci-dessous

public class AccessDeniedExceptionHandler implements AccessDeniedHandler
{

    private String errorPage;

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
            AccessDeniedException arg2) throws IOException, ServletException {
        response.sendRedirect(errorPage);
    }

       public void setErrorPage(String errorPage) {
       if ((errorPage != null) && !errorPage.startsWith("/")) {
            throw new IllegalArgumentException("errorPage must begin with '/'");
        }
        this.errorPage = errorPage;
    }

}

Lorsque je lance cette application, c'est l'erreur que je reçois. Je ne fais que coller une partie du stacktrace et des journaux Spring Debug. 

20:39:46,173 DEBUG AffirmativeBased:53 - Voter: org.springframework.security.access.vote.RoleVoter@5b7da0d1, returned: -1
20:39:46,173 DEBUG AffirmativeBased:53 - Voter: org.springframework.security.access.vote.AuthenticatedVoter@14c92844, returned: 0
20:39:46,178 DEBUG ExceptionTranslationFilter:154 - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.Java:71)
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.Java:204)

Comment puis-je résoudre ce problème? Premièrement, je veux empêcher le printemps de lancer cette exception. S'il le lance encore, je veux le gérer et ne pas lever de drapeau. 

Mise à jour: j'ai également joint une partie de mon fichier web.xml. 

<!-- Hibernate filter configuration -->

<filter>
        <filter-name>HibernateFilter</filter-name>
        <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HibernateFilter</filter-name> 
        <url-pattern>/*</url-pattern>       
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

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

    <!--Dispatcher Servlet -->

   <servlet>
     <servlet-name>rowz</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     <load-on-startup>1</load-on-startup>
   </servlet>
15
Ritesh M Nayak

Dans votre configuration, vous exigez que l'utilisateur soit always authentifié lors de la saisie de any URL sur votre site:

<security:intercept-url pattern="/**" access="ROLE_USER" />

Je pense que vous devriez autoriser l'utilisateur à être non authentifié when en entrant dans la page de connexion :

<security:intercept-url pattern="/your-login-page-url" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/your-login-process-url" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/your-login-failure-url" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/**" access="ROLE_USER" />

Si vous utilisez des URL telles que: /login/start, /login/error et /login/failure, vous pouvez avoir:

<security:intercept-url pattern="/login/**" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/**" access="ROLE_USER" />

Mise à jour:

Avoir cette configuration devrait permettre au framework de rediriger tous les utilisateurs non authentifiés (anonymes) vers la page de connexion, et tous authentifiés vers AccessDeniedHandler . La AccessDeniedException est l’une des parties essentielles du cadre et l’ignorer n’est pas une bonne idée. Il est difficile d’aider davantage si vous ne fournissez qu’une partie de votre configuration Spring Security.

Assurez-vous de lire le JavaDoc pour ExceptionTranslationFilter pour une explication détaillée des exceptions générées par le cadre, du pourquoi et du comment traitées par défaut.

Si possible, essayez de supprimer autant de parties personnalisées que vous avez ajoutées, telles que AuthenticationSuccessHandler , RememberMeAuthenticationFilter et AccessDeniedHandler et voyez si le problème persiste? Essayez d’obtenir la congiuration minimale et d’ajouter de nouvelles fonctionnalités étape par étape pour voir d’où vient l’erreur.

Une chose importante que vous ne mentionnez pas dans votre question est quel est le résultat de ce message d'erreur? Avez-vous HTTP 500? Ou HTTP 403? Ou êtes-vous redirigé vers la page de connexion?

Si, comme vous l'avez mentionné dans la question, l'utilisateur n'est pas authentifié et qu'il est redirigé vers la page de connexion, c'est ainsi qu'il est censé fonctionner. Il semble que vous obtenez le message d'erreur consigné par ExceptionTranslationFilter: 172 uniquement parce que vous avez défini le niveau DEBUG sur les classes de sécurité Spring. Si tel est le cas, cela devrait également fonctionner ainsi, et si vous ne voulez pas que l'erreur soit consignée, augmentez simplement le niveau de consignation pour les classes Spring Secyruty.

Mise à jour 2:

Les modèles avec filters="none" doivent correspondre aux attributs login-page, login-processing-url et authentication-failure-ur définis dans <security:form-login /> pour pouvoir ignorer tous les contrôles SpringSecurity sur les pages affichant la page de connexion et procéder à la connexion.

<security:http auto-config='true'>
  <security:intercept-url pattern="/static/**" filters="none"/>
  <security:intercept-url pattern="/index" filters="none"/>
  <security:intercept-url pattern="/j_spring_security_check" filters="none"/>
  <security:intercept-url pattern="/**" access="ROLE_USER" />

  <security:form-login login-page="/index"
        default-target-url="/home" always-use-default-target="true"
        authentication-success-handler-ref="AuthenticationSuccessHandler"        
        login-processing-url="/j_spring_security_check" 
        authentication-failure-url="/index?error=true"/>

   <security:remember-me key="myLongSecretCookieKey" token-validity-seconds="1296000" 
        data-source-ref="jdbcDataSource" user-service-ref="AppUserDetailsService" />

   <security:access-denied-handler ref="myAccessDeniedHandler" />   

</security:http>
12
Roadrunner

AccessDeniedHandler est invoquée lorsque l'utilisateur est connecté et qu'il n'y a aucune autorisation d'accès à la ressource ( source here ). Si vous souhaitez gérer la demande de connexion page lorsque l'utilisateur n'est pas connecté , configurez simplement dans contexte de sécurité:

<http ... entry-point-ref="customAuthenticationEntryPoint">

Et définissez customAuthenticationEntryPoint:

<beans:bean id="customAuthenticationEntryPoint" class="pl.wsiadamy.webapp.controller.util.CustomAuthenticationEntryPoint">
</beans:bean>

TIP, n'essayez pas de vous battre avec ExceptionTranslationFilter. J'ai essayé de remplacer org.springframework.security.web.access.ExceptionTranslationFilter, sans effets:

<beans:bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
  <beans:property name="authenticationEntryPoint"  ref="customAuthenticationEntryPoint"/>
  <beans:property name="accessDeniedHandler" ref="accessDeniedHandler"/>
</beans:bean>
<beans:bean id="accessDeniedHandler"
 class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
  <beans:property name="errorPage" value="/accessDenied.htm"/>
</beans:bean>

Le ref="customAuthenticationEntryPoint" n'a tout simplement pas été invoqué.

6
Athlan

J'ai ajouté Spring Access denied page de la manière suivante: . Spring Frame Work: 3.1 . Spring Security: 3.1, Java 1.5+.

Entrée dans * -security.xml:

<security:access-denied-handler error-page="/<My Any error page controller name>" />

Exemple: 

<security:access-denied-handler error-page="/accessDeniedPage.htm" />

La page d'erreur commencera toujours par "/"

Entrée pour le contrôleur:

@Controller
public class RedirectAccessDenied {

    @RequestMapping(value = "/accessDeniedPage.htm", method = RequestMethod.GET)
    public String redirectAccessDenied(Model model) throws IOException, ServletException {
        System.out.println("############### Redirect Access Denied Handler!");
        return "403";
    }
}

Ici 403 est mon nom JSP. 

3
Vivek Dandale

Spring Security utilise un objet AuthenticationEntryPoint pour décider quoi faire lorsqu'un utilisateur requiert une authentification. Vous pouvez créer votre propre bean AuthenticationEntryPoint ( voir javadoc ), puis définir l'attribut entryPoint dans l'élément http:

<http entry-point-ref="entryPointBean" .... />

Cependant, par défaut, l'élément form-login crée un LoginUrlAuthenticationEntryPoint qui redirige tous vos utilisateurs non authentifiés vers la page de connexion. Vous ne devriez donc pas avoir à le faire vous-même. En fait, le journal que vous avez publié affirme qu'il transfère l'utilisateur au point d'entrée d'authentification: "L'accès est refusé (l'utilisateur est anonyme); redirection au point d'entrée d'authentification". 

Je me demande si le problème est que vous avez désactivé la chaîne de filtrage pour l'URL de connexion. Au lieu de définir aucun filtre, ce qui signifie que la sécurité du ressort est entièrement contournée, essayez de conserver les filtres tout en permettant un accès illimité comme ceci:

<security:intercept-url pattern="/login" access="permitAll" />

Si le problème persiste, envoyez le reste du journal pour que nous puissions voir ce qui se passera une fois la demande transférée au point d'entrée.

2
MicGer

Solution par programme:

@Order(1)
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //
    // ...
    //

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandlerImpl() {
            @Override
            public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                super.handle(request, response, accessDeniedException);
                accessDeniedException.printStackTrace();
            }
        });

        //
        // ...
        //

    }

}
1
Marcelo C.

Pouvez-vous vérifier que votre web.xml prend en charge la demande de transfert?

errorPage est une requête FORWARD et principalement dans web.xml, nous ne prenons en charge que REDIRECTS. Juste une pensée, votre code me semble correct. 

Édité 

Un point de vue différent et Ceci est extrait du code de travail uniquement. Regardez Classe de votants authentifiés

Désactiver les annotations 

<global-method-security pre-post-annotations="disabled"
    secured-annotations="disabled" access-decision-manager-ref="accessDecisionManager">
</global-method-security>

contourner les filtres 

<http auto-config="true" use-expressions="true"
    access-decision-manager-ref="accessDecisionManager"
    access-denied-page="/accessDenied">
    <intercept-url pattern="/appsecurity/login.jsp" filters="none" />
    <intercept-url pattern="/changePassword" filters="none" />
    <intercept-url pattern="/pageNotFound" filters="none" />
    <intercept-url pattern="/accessDenied" filters="none" />
    <intercept-url pattern="/forgotPassword" filters="none" />
    <intercept-url pattern="/**" filters="none" />


    <form-login login-processing-url="/j_spring_security_check"
        default-target-url="/home" login-page="/loginDetails"
        authentication-failure-handler-ref="authenticationExceptionHandler"
        authentication-failure-url="/?login_error=t" />
    <logout logout-url="/j_spring_security_logout"
        invalidate-session="true" logout-success-url="/" />
    <remember-me />
    <!-- Uncomment to limit the number of sessions a user can have -->
    <session-management invalid-session-url="/">
        <concurrency-control max-sessions="1"
            error-if-maximum-exceeded="true" />
    </session-management>
</http>

Électeur de décision personnalisé 

<bean id="customVoter" class="xyz.appsecurity.helper.CustomDecisionVoter" />

Gestionnaire de décision d'accès 

<!-- Define AccessDesisionManager as UnanimousBased -->
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
    <property name="decisionVoters">
        <list>
            <ref bean="customVoter" />
            <!-- <bean class="org.springframework.security.access.vote.RoleVoter" 
                /> -->
            <bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
        </list>
    </property>
</bean>

Gestionnaire d'exception d'authentification 

<bean id="authenticationExceptionHandler"
    class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
    <property name="exceptionMappings">
        <props>
            <!-- /error.jsp -->
            <prop
                key="org.springframework.security.authentication.BadCredentialsException">/?login_error=t</prop>
            <!-- /getnewpassword.jsp -->
            <prop
                key="org.springframework.security.authentication.CredentialsExpiredException">/changePassword</prop>
            <!-- /lockedoutpage.jsp -->
            <prop key="org.springframework.security.authentication.LockedException">/?login_error=t</prop>
            <!-- /unauthorizeduser.jsp -->
            <prop
                key="org.springframework.security.authentication.DisabledException">/?login_error=t</prop>
        </props>
    </property>
</bean>
0
Manish Singh

Il semblerait que spring tente de rediriger les utilisateurs qui ne se sont pas connectés vers la page de connexion, qui est "/ index", mais qui est elle-même une URL protégée.

L'autre possibilité est, il essaye d'afficher /public/403.html, mais c'est à nouveau protégé par la configuration de sécurité.

Pouvez-vous ajouter les entrées suivantes et essayer?

<security:intercept-url pattern="/login" filters="none" />
<security:intercept-url pattern="/public/**" filters="none" />
0
Raghuram