Не допустить вызова метода без исключения с помощью @авторизацию аннотации

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

есть идеи, как это предотвратить?


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

3 ответов


решение заключается в использовании custom MethodSecurityInterceptor, который называет AccessDecisionManager (неявно, bu вызывает метод 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 (и создать большую часть соответствующей структуры bean, которую исходный тег делает по умолчанию):

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

Ok Я нашел способ предотвратить 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>

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


это код для решения советов я применил.

это код аспекта:

@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 (value=1) выполняет работу). Также вам нужно будет добавить aspectj autorproxy в контекст приложения:

<aop:aspectj-autoproxy/>

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

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

надеюсь, что это полезно для кого-то.