使用@PreAuthorize Annotation防止没有异常的方法调用

时间:2011-01-07 00:18:29

标签: spring spring-security

我们正在使用Spring Security 3.我们有一个 PermissionEvaluator 的自定义实现,它具有这种复杂的算法,可以在应用程序的方法级别授予或拒绝访问权限。为此,我们将 @PreAuthorize 注释添加到我们想要保护的方法(显然)。一切都很好。但是,我们正在寻找的行为是,如果拒绝 hasPermission 调用,则只需要跳过受保护的方法调用,而不是每次发生时都会收到403错误。

任何想法如何预防?


你可以在这里找到对问题的不同解释; AccessDeniedException handling during methodSecurityInterception

3 个答案:

答案 0 :(得分:4)

解决方案是使用自定义MethodSecurityInterceptor,它调用AccessDecisionManager(隐式地,调用超级方法)并决定是否继续进行方法调用。

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

答案 1 :(得分:2)

好的,我发现了一种防止AccessDeniedException的方法。 然而,这并没有解决问题。现在,其余代码的执行是正常的,但即使hasPermission返回false,也不会阻止安全方法调用。

这就是我设法阻止AccessDeniedException停止所有内容的方法:

您需要实施AccessDecisionManager,以阻止<​​strong> 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>

有关如何在不停止所有内容的情况下阻止调用该方法的任何想法?

答案 2 :(得分:2)

这是我实施的建议解决方案的代码。

这是Aspect代码:

@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添加到App上下文中:

<aop:aspectj-autoproxy/>

您可能还需要使用 @Around 参数,在我的情况下,这非常简单,因为我们使用 PreAuthorize 注释保护所有内容。

这是我能想到的最简单的方法。但是,我强烈建议人们使用Boris Kirzner建议的解决方案。

希望这对某人有帮助。