2011-01-07 1 views
6

Wir verwenden Spring Security 3. Wir haben eine benutzerdefinierte Implementierung von PermissionEvaluator, die diesen komplexen Algorithmus gewährt oder verweigert Zugriff auf Methodenebene auf der Anwendung. Um das zu tun, fügen wir eine @ PreAuthorize Annotation zu der Methode, die wir (natürlich) schützen möchten, hinzu. Alles ist in Ordnung. Allerdings ist das Verhalten, das wir suchen, dass, wenn ein hasPermission Aufruf abgelehnt wird, der geschützte Methodenaufruf nur übersprungen werden muss, stattdessen erhalten wir einen Fehler 403 jedes Mal, wenn das passiert.Verhindern Methodenaufruf ohne Ausnahme mit @ PreAuthorize Annotation

Irgendwelche Ideen, wie man das verhindert?


Hier finden Sie eine andere Erklärung des Problems; AccessDeniedException handling during methodSecurityInterception

Antwort

4

Die Lösung ist benutzerdefinierte MethodSecurityInterceptor zu verwenden, die die AccessDecisionManager aufruft (implizit bu Aufruf Super-Methode) und entscheidet, ob mit einem Methodenaufruf fortfahren.

package com.myapp; 

public class MyMethodSecurityInterceptor extends MethodSecurityInterceptor { 

    @Override 
    public Object invoke(MethodInvocation mi) throws Throwable { 
     Object result = null; 
     try { 
      InterceptorStatusToken token = super.beforeInvocation(mi);    
     } catch (AccessDeniedException e) { 
      // access denied - do not invoke the method and return null 
      return null; 
     } 

     // access granted - proceed with the method invocation 
     try { 
      result = mi.proceed(); 
     } finally { 
      result = super.afterInvocation(token, result); 
     } 

     return result;   
     } 
} 

die App Kontext Einrichten ist ein bisschen schwierig: Da Sie nicht <sec:global-mathod-security> in diesem Fall benutzen kann, gibt es eine Notwendigkeit, eine explizite AOP-Konfiguration (und schaffen die meisten der entsprechenden Bean Struktur der Original-Tag tut zu definieren standardmäßig):

<aop:config> 
    <!-- Intercept all relevant methods --> 
    <aop:pointcut id="myMethods" 
        expression='execution(* com.myapp.myService+.*(..))'/> 
    <aop:advisor advice-ref="mySecurityInterceptor" pointcut-ref="myMethods"/> 
</aop:config> 

<!-- Configure custom security interceptor --> 
<bean id="mySecurityInterceptor" 
     class="com.myapp.MyMethodSecurityInterceptor"> 
    <property name="securityMetadataSource"> 
     <bean class="org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource"> 
      <constructor-arg> 
       <bean class="org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory"> 
        <constructor-arg> 
         <bean class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler"/> 
        </constructor-arg> 
       </bean> 
      </constructor-arg> 
     </bean> 
    </property> 
    <property name="validateConfigAttributes" value="false"/> 
    <property name="accessDecisionManager" ref="accessDecisionManager"/> 
    <property name="authenticationManager" ref="authenticationManager"/> 
</bean> 

<!-- Configure AccessDecisionManager --> 
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased"> 
    <property name="decisionVoters"> 
     <list> 
      <bean class="org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter"> 
       <constructor-arg> 
        <bean class="org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice"/> 
       </constructor-arg> 
      </bean> 
     </list> 
    </property> 
</bean> 

<!-- Configure AuthenticationManager as you wish --> 
<!-- ........................................... --> 
+1

Ich endete etwas viel einfacher zu tun, ich habe nur einen Hinweis über den MethodSecurityInterceptor hinzugefügt, der die Ausnahme mit einem @Around abfängt, so dass die Ausbreitung über diesen Punkt hinaus verhindert wird . Auf diese Weise konnte ich vermeiden, Spring Security zu Fuß zu konfigurieren, was sehr kompliziert ist. Was Sie jedoch vorschlagen, ist der kanonische Weg, es zu tun und es funktioniert. – Chepech

+1

Sehr schön! Ich frage mich, ob wir das Verhalten von beforeInvocation() (mit AOP) ändern können, um Benutzerberechtigungen für bestimmte Methoden (die OP schützen möchte) zu überprüfen und mi durch eine Dummy MethodInvocation-Instanz zu ersetzen, anstatt sich um AccessDeniedException zu kümmern. – Ritesh

+0

@Chepech @Around Lösung scheint sehr einfach und elegant. Wenn Sie den betreffenden @Around-Code aktualisieren, werde ich ihn gerne abstimmen. – Ritesh

2

Ok Ich habe einen Weg gefunden, die AccessDeniedException zu verhindern. Dies löst jedoch nicht das Problem. Die Ausführung des restlichen Codes wird nun standardmäßig fortgesetzt, der gesicherte Methodenaufruf wird jedoch nicht verhindert, selbst wenn hasPermission den Wert false zurückgibt.

Dies ist, wie ich die AccessDeniedException von stoping alles zu verhindern, verwaltet:

Sie benötigen einen AccessDecisionManager zu implementieren, wo Sie die AccessDeniedException Ausbreitung zu verhindern. Das ist der einfache Teil. Meine sieht so aus:

public class SkipMethodCallAccessDecisionManager extends AffirmativeBased { 
    @Override 
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes){ 
     try{ 
      super.decide(authentication, object, configAttributes); 
     }catch(AccessDeniedException adex){ 
      logger.debug("Access Denied on:" + object); 
     } 
    } 
} 

Dann der schwierige Teil ... Einrichten des Anwendungskontext.

<sec:global-method-security pre-post-annotations="enabled" access-decision-manager-ref="skipMethodCallAccessDecisionManager "/> 

<bean id="skipMethodCallAccessDecisionManager" class="com.application.auth.vote.SkipMethodCallAccessDecisionManager "> 
    <property name="decisionVoters"> 
     <list> 
      <bean class="org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter"> 
       <constructor-arg ref="expressionBasedPreInvocationAdvice"/> 
      </bean> 
      <!-- Insert RoleVoter if required --> 
      <bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>   
     </list> 
    </property> 
</bean> 

<bean id="expressionBasedPreInvocationAdvice" class="org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice"> 
    <property name="expressionHandler" ref="expressionHandler"/> 
</bean> 

Irgendwelche Ideen, um zu verhindern, dass die Methode aufgerufen wird, ohne alles zu stoppen?

+0

das wird nicht funktionieren. Wie von @Boris Kirzner gezeigt, wird mi.proceed() aufgerufen, wenn es keine Ausnahme gibt in beforeInvocation (mi) – Ritesh

+0

@Ritesh - Ich weiß, dass es nicht funktioniert, fügte ich dies zu dokumentieren, was ich tat, um Hinweise zu geben Leute, die die Frage gelesen haben, da niemand fast einen Monat lang geantwortet hat. Ich sage dies in der Antwort. – Chepech

+0

Ich wusste nicht, dass es alte Frage war. Ich habe es überprüft, als ich gegoogelt habe, um eine ähnliche Lösung zu finden. – Ritesh

2

Dies ist der Code für die von mir implementierte Beratungslösung.

Dies ist der Aspect-Code:

@Aspect 
public class AccessDeniedHaltPreventionAdvice { 
private final Log logger = LogFactory.getLog(AccessDeniedHaltPrevention.class); 

@Around("execution(@org.springframework.security.access.prepost.PreAuthorize * *(..))") 
public Object preventAccessDeniedHalting(ProceedingJoinPoint pjp) throws Throwable{ 
    Object retVal = null; 
    try{ 
     retVal = pjp.proceed(); 
    }catch(AccessDeniedException ade){ 
     logger.debug("** Access Denied ** "); 
    }catch(Throwable t){ 
     throw t; 
    } 
    return retVal; 
} 

}

Möglicherweise müssen Sie eine @Order Anmerkung hinzufügen, um sicherzustellen, dass die Beratung der Lage ist, die Ausnahme zu fangen (in der Regel ein @Order (Wert = 1) macht die Arbeit).Sie müssen auch die aspectj autorproxy zum App Kontext hinzuzufügen:

<aop:aspectj-autoproxy/> 

Sie auch mit den @Around Parameter zu spielen, um brauchen, In meinem Fall war es ziemlich einfach, wie wir alles sichern mit PreAuthorize Anmerkungen.

Dies ist die einfachste Möglichkeit, die ich herausfinden konnte. Ich empfehle jedoch dringend, die von Boris Kirzner vorgeschlagene Lösung zu verwenden.

Hope das ist hilfreich für jemanden.

+0

Ich habe versucht, dies an eine PostAuthorize-Annotation anzupassen, leider hat es nicht so gut funktioniert, da ich "javax.persistence.RollbackException: Transaktion als rollbackOnly markiert" bekomme, was merkwürdig ist, weil ich sehe, dass es die Ausnahme verschlingt , was im Allgemeinen die Transaktion im Rollback-Modus kitzelt. – creechy