2011-01-07 2 views
6

Мы используем Spring Security 3. У нас есть пользовательская реализация PermissionEvaluator, которая имеет этот сложный алгоритм для предоставления или запрета доступа на уровне метода в приложении. Для этого мы добавим аннотацию @PreAuthorize к методу, который мы хотим защитить (очевидно). Все в порядке. Однако поведение, которое мы ищем, заключается в том, что если вызов hasPermission запрещен, вызов защищенного метода нужно просто пропустить, вместо этого мы получаем ошибку 403 каждый раз, когда это происходит.Запретить вызов метода без исключения с помощью @PreAuthorize Аннотация

Любые идеи, как предотвратить это?


Здесь вы можете найти другое объяснение проблемы; AccessDeniedException handling during methodSecurityInterception

ответ

4

Решение состоит в использовании пользовательского MethodSecurityInterceptor, который вызывает AccessDecisionManager (неявно, метод bu call super) и решает, следует ли продолжать вызов метода.

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

Настройка контекста приложения является немного сложнее: так как вы не можете использовать <sec:global-mathod-security> в этом случае существует необходимость определить явную конфигурацию АОП (и создать большую часть соответствующей структуры боба оригинальный тег делает по умолчанию):

<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

Я закончил делать что-то более простое, я просто добавил совет по методу MethodSecurityInterceptor, который ловит исключение с помощью @Around, чтобы предотвратить распространение этой точки , Таким образом, я мог бы избежать настройки Spring Security пешком, что очень сложно. Однако то, что вы предлагаете, - это канонический способ сделать это, и он работает. – Chepech

+1

Очень приятно! Интересно, можно ли каким-либо образом изменить поведение beforeInvocation() (используя AOP), чтобы проверить права пользователя на определенный метод (который OP хочет защитить) и заменить mi на фиктивный экземпляр MethodInvocation вместо того, чтобы беспокоиться о AccessDeniedException. – Ritesh

+0

@Chepech @ Практическое решение кажется очень простым и элегантным. Если вы обновите соответствующий код @Around, я с радостью проголосую за него. – Ritesh

2

Хорошо, я нашел способ предотвратить AccessDeniedException. Однако это не решает проблему. Исключение остальной части кода теперь нормализуется, однако вызов защищенного метода не предотвращается, даже если hasPermission возвращает false.

Это, как мне удалось предотвратить AccessDeniedException от останавливая все:

Вам нужно реализовать AccessDecisionManager, где вы предотвратить AccessDeniedException распространения. Это легкая часть. Шахта выглядит так:

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

Тогда сложная часть ... настройка контекста приложения.

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

Любые идеи о том, как предотвратить вызов метода без остановки всего?

+0

это не сработает. Как показано @Boris Kirzner, mi.proceed() будет вызываться, если нет исключения в beforeInvocation (mi) – Ritesh

+0

@Ritesh - я знаю, что это не сработало, я добавил это, чтобы документировать то, что я делал, чтобы предоставить ключи к люди, которые читают вопрос, поскольку никто не ответил на это почти 1 месяц. Я утверждаю этот факт в ответе. – Chepech

+0

Я не понимал, что это старый вопрос. Я проверил его, когда искал поисковые запросы, чтобы найти подобное решение. – Ritesh

2

Это код решения для консультаций, которое я внедрил.

Это код Аспект:

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

}

Вам может понадобиться добавить @Order аннотацию, чтобы гарантировать, что совет может поймать исключение (обычно @Order (значение = 1) делает работу).Кроме того, вам нужно добавить autorproxy AspectJ в контексте App:

<aop:aspectj-autoproxy/> 

Вам также может понадобиться, чтобы поиграть с @Around параметров, в моем случае это было довольно просто, как мы обеспечиваем все с Предотвращение аннотации.

Это самый простой способ выяснить. Тем не менее, я настоятельно рекомендую людям использовать решение, предложенное Борисом Кирзнером.

Надеюсь, это кому-то поможет.

+0

Я пробовал адаптировать это для использования в аннотации PostAuthorize, к сожалению, это не сработало так хорошо, поскольку я получаю «javax.persistence.RollbackException: транзакция, отмеченная как rollbackOnly», что странно, потому что я вижу, что она проглатывает исключение , что, как правило, то, что щелкает транзакцию в режиме отката. – creechy

Смежные вопросы