2011-01-07 45 views
6

Chúng tôi đang sử dụng bảo mật mùa xuân 3. Chúng tôi có triển khai tùy chỉnh PermissionEvaluator có thuật toán phức tạp này để cấp hoặc từ chối truy cập ở cấp phương thức trên ứng dụng. Để làm điều đó, chúng tôi thêm chú thích @PreAuthorize vào phương pháp mà chúng tôi muốn bảo vệ (hiển nhiên). Tất cả mọi thứ là tốt về điều đó. Tuy nhiên, hành vi mà chúng tôi đang tìm kiếm là nếu cuộc gọi hasPermission bị từ chối, cuộc gọi phương thức được bảo vệ chỉ cần bị bỏ qua, thay vào đó chúng tôi nhận được lỗi 403 mỗi lần xảy ra.Ngăn chặn gọi phương thức mà không có ngoại lệ bằng cách sử dụng chú thích @PreAuthorize

Bất kỳ ý tưởng nào về cách ngăn chặn điều đó?


Bạn có thể tìm thấy giải thích khác về vấn đề ở đây; AccessDeniedException handling during methodSecurityInterception

Trả lời

4

Giải pháp là sử dụng tùy chỉnh MethodSecurityInterceptor, gọi AccessDecisionManager (ngầm, bu gọi phương thức siêu) và quyết định xem có nên tiếp tục cuộc gọi phương thức hay không.

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

Thiết lập bối cảnh ứng dụng là một chút khó khăn: kể từ khi bạn không thể sử dụng <sec:global-mathod-security> trong trường hợp này, có một cần phải xác định một cấu hình AOP rõ ràng (và tạo ra hầu hết các cấu trúc đậu tương ứng thẻ original làm theo mặc định):

<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

Tôi đã làm một điều gì đó khá đơn giản hơn, tôi vừa thêm một lời khuyên về MethodSecurityInterceptor để bắt ngoại lệ với @Around sao cho quá trình truyền qua điểm đó bị ngăn chặn . Bằng cách đó tôi có thể tránh việc cấu hình Spring Security bằng chân, điều này rất phức tạp. Tuy nhiên những gì bạn đề xuất là cách kinh điển để làm điều đó và nó hoạt động. – Chepech

+1

Rất đẹp! Tôi tự hỏi nếu có cách nào chúng ta có thể thay đổi hành vi của beforeInvocation() (sử dụng AOP) để kiểm tra quyền của người dùng trên phương thức cụ thể (OP muốn bảo vệ) và thay thế mi bằng một instance MethodInvocation giả thay vì lo lắng về AccessDeniedException. – Ritesh

+0

@Chepech @Around giải pháp có vẻ rất đơn giản và thanh lịch. Nếu bạn cập nhật mã @Around được đề cập, tôi sẽ sẵn sàng bỏ phiếu. – Ritesh

2

Ok tôi đã tìm thấy cách ngăn chặn AccessDeniedException. Tuy nhiên điều này không giải quyết được vấn đề. Việc truy tố phần còn lại của mã bây giờ contunies normaly, tuy nhiên cuộc gọi phương thức bảo đảm không được ngăn chặn ngay cả khi hasPermission trả về false.

Đây là cách tôi quản lý để ngăn chặn sự AccessDeniedException từ stoping tất cả mọi thứ:

Bạn cần phải thực hiện một AccessDecisionManager nơi bạn ngăn chặn sự AccessDeniedException tuyên truyền. Đó là phần dễ dàng. Tôi trông giống như sau:

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

Sau đó, phần khó khăn ... thiết lập ngữ cảnh ứng dụng.

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

Bất kỳ ý tưởng nào về cách ngăn chặn phương thức được gọi mà không dừng mọi thứ?

+0

tính năng này sẽ không hoạt động. Như được hiển thị bởi @Boris Kirzner, mi.proceed() sẽ được gọi nếu không có ngoại lệ trong beforeInvocation (mi) – Ritesh

+0

@Ritesh - Tôi biết nó không hoạt động, tôi đã thêm điều này vào tài liệu những gì tôi đang làm để cung cấp manh mối những người đọc câu hỏi vì không ai trả lời câu hỏi này trong gần 1 tháng. Tôi tuyên bố thực tế này trong câu trả lời. – Chepech

+0

Tôi không nhận ra đó là câu hỏi cũ. Tôi đã kiểm tra nó khi tôi googling để tìm một giải pháp tương tự. – Ritesh

2

Đây là mã cho giải pháp tư vấn tôi đã triển khai.

Đây là mã 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; 
} 

}

Bạn có thể cần phải thêm một @Order chú thích để đảm bảo rằng những lời khuyên có khả năng nắm bắt những ngoại lệ (thường là một @Order (giá trị = 1) thực hiện công việc).Ngoài ra, bạn sẽ cần phải thêm autorproxy AspectJ với bối cảnh App:

<aop:aspectj-autoproxy/> 

Bạn cũng có thể cần phải chơi xung quanh với @Around thông số, Trong trường hợp của tôi nó là khá đơn giản như chúng ta được đảm bảo tất cả mọi thứ với PreAuthorize chú thích.

Cách đơn giản nhất tôi có thể tìm ra. Tuy nhiên, tôi khuyên mọi người nên sử dụng giải pháp được đề xuất bởi Boris Kirzner.

Hy vọng điều này hữu ích đối với ai đó.

+0

Tôi đã cố gắng điều chỉnh điều này để sử dụng trên chú thích PostAuthorize, tiếc là nó không hoạt động tốt như vậy, vì tôi nhận được "javax.persistence.RollbackException: Giao dịch được đánh dấu là rollbackOnly", điều này thật lạ vì tôi thấy nó nuốt vào ngoại lệ , thường là những gì cù các giao dịch vào chế độ Rollback. – creechy

Các vấn đề liên quan