Prevent Method call without Exception using @PreAuthorize Annotation

前端 未结 3 1691
抹茶落季
抹茶落季 2021-01-01 02:25

We are using Spring Security 3. We have a custom implementation of PermissionEvaluator that has this complex algorithm to grant or deny access at method lev

相关标签:
3条回答
  • 2021-01-01 02:41

    The solution is to use custom MethodSecurityInterceptor, which calls the AccessDecisionManager (implicitly, bu calling super's method) and decides than whether to proceed with a method call.

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

    Setting up the app context is a bit tricky: since you can not use <sec:global-mathod-security> in this case, there is a need to define an explicit AOP configuration (and create most of the corresponding bean structure the original tag does by default):

    <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 -->
    <!-- ........................................... -->
    
    0 讨论(0)
  • 2021-01-01 02:44

    This is the code for the advice solution I implemented.

    This is the 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;
    }
    

    }

    You may need to add a @Order annotation to ensure that the advice is able to catch the exception (usually a @Order(value=1) does the work). Also you'll need to add the aspectj autorproxy to the App context:

    <aop:aspectj-autoproxy/>
    

    You may also need to play around with the @Around parameters, In my case it was pretty simple as we are securing everything with PreAuthorize annotations.

    This the simplest way I could figure out. However, I strongly recommend people to use the solution suggested by Boris Kirzner.

    Hope this is helpful to someone.

    0 讨论(0)
  • 2021-01-01 02:51

    Ok I found a way to prevent the AccessDeniedException. However this doesnt solves the problem. The excecution of the rest of the code now contunies normaly, however the secured method call is not prevented even when hasPermission returns false.

    This is how I managed to prevent the AccessDeniedException from stoping everything:

    You need to implement an AccessDecisionManager where you prevent the AccessDeniedException propagation. Thats the easy part. Mine looks like this:

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

    Then the tricky part... setting up the application context.

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

    Any ideas on how to prevent the method from being called without stopping everything?

    0 讨论(0)
提交回复
热议问题