복붙노트

[SPRING] @PreAuthorize 주석을 사용하여 예외없이 메소드 호출 방지

SPRING

@PreAuthorize 주석을 사용하여 예외없이 메소드 호출 방지

우리는 Spring Security 3을 사용하고 있습니다. 애플리케이션의 메소드 레벨에서 액세스를 허용하거나 거부하는 복잡한 알고리즘을 가진 PermissionEvaluator의 맞춤 구현이 있습니다. 이를 위해 @PreAuthorize 주석을 보호하려는 메소드에 추가합니다 (분명히). 그것에 모든 것이 좋습니다. 그러나 우리가 찾고있는 동작은 hasPermission 호출이 거부되면 보호 된 메서드 호출 만 건너 뛰고 그 대신 매번 403 오류가 발생한다는 것입니다.

어떤 아이디어가 그것을 방지하는 방법?

여기서 문제에 대한 다른 설명을 찾을 수 있습니다. methodSecurityInterception 중 AccessDeniedException 처리

해결법

  1. ==============================

    1.해결책은 사용자 정의 MethodSecurityInterceptor를 사용하는 것인데,이 메소드는 AccessDecisionManager를 호출하고 (암시 적으로, super 메소드를 호출하는) 메소드 호출을 진행할지 여부를 결정합니다.

    해결책은 사용자 정의 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;        
            }
    }
    

    앱 컨텍스트를 설정하는 것은 약간 까다 롭습니다.이 경우 를 사용할 수 없으므로 명시 적 AOP 구성을 정의해야합니다 (그리고 해당 빈 구조의 대부분을 원래 태그로 생성해야합니다 기본적으로) :

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

    2.좋아, AccessDeniedException을 방지하는 방법을 찾았습니다. 그러나이 문제는 문제를 해결하지 못합니다. 나머지 코드는 예외적으로 정상적으로 작동하지만 hasPermission이 false를 반환하는 경우에도 보안 된 메서드 호출이 방지되지 않습니다.

    좋아, AccessDeniedException을 방지하는 방법을 찾았습니다. 그러나이 문제는 문제를 해결하지 못합니다. 나머지 코드는 예외적으로 정상적으로 작동하지만 hasPermission이 false를 반환하는 경우에도 보안 된 메서드 호출이 방지되지 않습니다.

    이것은 AccessDeniedException이 모든 것을 멈추지 않게하는 방법입니다.

    AccessDeniedException 전파를 막는 AccessDecisionManager를 구현해야합니다. 그게 쉬운 부분. 내 모습은 다음과 같습니다.

    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>
    

    모든 것을 중단하지 않고 메서드가 호출되는 것을 방지하는 방법에 대한 아이디어가 있습니까?

  3. ==============================

    3.이것은 구현 한 조언 솔루션의 코드입니다.

    이것은 구현 한 조언 솔루션의 코드입니다.

    이것은 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 주석을 추가하여 조언이 예외를 catch 할 수 있도록해야 할 수 있습니다 (일반적으로 @Order (value = 1)이 작업을 수행함). 또한 aspectj autorproxy를 App 컨텍스트에 추가해야한다.

    <aop:aspectj-autoproxy/>
    

    @Around 매개 변수로 재생해야 할 수도 있습니다. 필자의 경우 PreAuthorize 주석을 사용하여 모든 것을 보호하기 때문에 매우 간단했습니다.

    이것은 내가 알아낼 수있는 가장 간단한 방법입니다. 그러나 Boris Kirzner가 제안한 솔루션을 사용하는 것이 좋습니다.

    희망이 누군가에게 도움이됩니다.

  4. from https://stackoverflow.com/questions/4621394/prevent-method-call-without-exception-using-preauthorize-annotation by cc-by-sa and MIT license