Запрет вызова метода без исключения с использованием аннотации @PreAuthorize - PullRequest
6 голосов
/ 07 января 2011

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

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


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

Ответы [ 3 ]

4 голосов
/ 20 февраля 2011

Решение состоит в том, чтобы использовать пользовательский MethodSecurityInterceptor, который вызывает AccessDecisionManager (неявно, но вызывающий метод 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 -->
<!-- ........................................... -->
2 голосов
/ 26 февраля 2011

Это код для внедренного мной решения.

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

@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 в контекст приложения:

<aop:aspectj-autoproxy/>

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

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

Надеюсь, это кому-нибудь пригодится.

2 голосов
/ 07 января 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...