【问题标题】:Convert Spring Security AccessDeniedException ( with GWT )转换 Spring Security AccessDeniedException ( 使用 GWT )
【发布时间】:2013-07-05 21:25:08
【问题描述】:

我正在使用 Google Web Toolkit 并使用 spring security 保护服务器。我想在我的业务对象方法中使用 @PreAuthorize - 注释。

这里的问题是,GWT 不能序列化 AccessDeniedException(如果用户无权调用此方法则抛出)。所以我需要在每个调用BO方法的RPC服务方法中实现一个额外的try-catch,手动转换异常。我真的很想避免这种额外的try-catch!

更多解释:

BO(使用 @PreAuthorize 保护)> GWT RPC 调用,需要额外的 try-catch 来转换异常。

我发现的只是将特定异常映射到特定页面的映射器,但没有关于如何将异常转换为另一个异常的信息(或提供我自己的实现来抛出我的自定义异常)。

我希望我能用你们能理解的方式描述我的问题。

我找到了这个帖子:

http://forum.springsource.org/showthread.php?85937-Method-Security-Throw-custom-exception-instead-of-AccessDeniedException

但 AccessDecisionManager 只抛出 AccessDeniedException,我无法获得有关如何根据我的需要自定义它的建议。

提前致谢!

【问题讨论】:

    标签: gwt spring-security


    【解决方案1】:

    我不知道有什么方法可以告诉 Spring Security 使用其他类型而不是 AccessDeniedException。所以我认为你需要在某个地方放置 try/catch 块并更改异常类型。有多个地方可以这样做:

    • 您可以覆盖MethodSecurityInterceptor,然后覆盖configure Spring Security to use your class。这里可以抛出任何异常类型,所以转换没有问题。但我认为做conf会更难。
    • 如果您使用的解决方案是所有传入的 RPC 调用都通过一个 servlet/控制器(如 here),那么您可以在此处应用转换逻辑。
    • 也许最优雅的解决方案是为每个必要的 bean 应用自定义 AOP 拦截器(可以进行异常包装)。这个拦截器必须实现org.springframework.core.Ordered接口,它可以使用HIGHEST_PRECEDENCE常量来确保它会在MethodSecurityInterceptor之前执行。有关排序的更多详细信息,请参阅this entry(看起来相同的 Ordered 接口用于两种情况:AspectJ 和标准 AOP)。使用@Around("@annotation(org.springframework.security.access.prepost.PreAuthorize)"),您可以将自定义拦截器直接应用于 PreAuthorize 注释,因此无需额外配置。

    【讨论】:

    • 传入的 RPC 调用分为几个“逻辑模块”,所以我需要实现多个 try-catch 场景,它们都会做同样的事情。但是“afterInvocationManager”看起来很不错。将尝试并稍后回到这里。谢谢!
    • 不客气。也许最优雅的解决方案是为每个必要的 bean 应用自定义 AOP 拦截器(可以进行异常包装)。由我不知道如何确保它会在MethodSecurityInterceptor 之前执行。
    • 有一种方法可以定义 AOP 拦截器的顺序,所以我更新了我的答案。
    • 在四处搜索(并阅读此:denksoft.com/articles/acl-spring-security-tutorial)之后,简单地尝试转换或抛出与 AccessDeniedException 不同的异常似乎需要大量工作和巨大开销。真的有必要自己实现所有步骤吗?我只想实现一个自定义 AfterInvocationProviderManager 并捕获 AccessDeniedException 并抛出一个自定义异常。但是在为 ProviderManager 定义 bean 时,我需要提供一个提供者列表。我该怎么做才能告诉 spring 使用默认值?
    • 来自配置文件:'' 导致错误:'Caused by: java.lang.IllegalArgumentException: A list of AfterInvocationProviders 是必需的'。当提供一个空列表时,同样的错误。
    【解决方案2】:

    我终于用 MethodInterceptor 做到了(感谢 Maksym Demidas)。我的解决方案如下:

    我的自定义 MethodInterceptor 将包装所有调用并仅捕获“GWT-non-serializable”AccessDeniedException 以将其转换为标准异常:

    public class CustomInterceptor implements MethodInterceptor {
    
    @Override
    public Object invoke(MethodInvocation arg0) throws Throwable {
    
        try {
            System.out.println("********* INTERCEPT: "
                    + arg0.getMethod().getName());
            return arg0.proceed();
        } catch (AccessDeniedException e) {
            e.printStackTrace();
            System.out.println("AccessDeniedException for method: "
                    + arg0.getMethod().getName());
            throw new Exception("Access denied!");
        }
    }
    

    还有来自applicationContext.xml的重要部分:

        <!-- Define a bean with our custom MethodInterceptor -->
    <bean id="debuglnterceptor" class="com.my.server.CustomInterceptor" />
    
    <!-- Register our custom MethodInterceptor to wrap all methods called from 
        com.my.server.services classes -->
    <aop:config>
        <aop:advisor advice-ref="debuglnterceptor"
            pointcut="execution(* com.my.server.services..*(..))" />
    </aop:config>
    

    【讨论】:

    • +1 用于拦截器解决方案。很高兴看到它对您有用。关于共享代码的两个要点:1)引入一些自定义 ClientAccessDeniedException 并使用它而不是 Eception 可能更方便 2)实现org.springframework.core.Ordered 并确保您的CustomInterceptor 将始终被解雇可能会更好 之后 MethodSecurityInterceptor
    【解决方案3】:

    这里是Maksim Demidas使用Spring AOP开发的解决方案:

    定义“抛出建议”类型的 Spring AOP 建议。它实现接口 Ordered with HIGHEST_PRECEDENCE 以正确集成其他建议,例如 @Secured@PreAuthorize

    public class AccessDeniedExceptionInterceptor implements ThrowsAdvice, Ordered {
    
        public void afterThrowing(AccessDeniedException e) throws GwtAccessDeniedException {
    
            throw new GwtAccessDeniedException();
        }
    
        @Override
        public int getOrder() {
            return HIGHEST_PRECEDENCE;
        }
    }
    

    在 Spring 上下文文件中,将拦截器定义为 bean:

    <bean id="accessDeniedExceptionInterceptor" class="my.interceptor.package.AccessDeniedExceptionInterceptor"/>
    

    在 Spring 上下文文件中,配置安全对象。在此示例中,特定实现的所有公共方法都是安全的:

    <!-- Configure global method security for the secured object -->
    <security:global-method-security>
        <security:protect-pointcut access="ROLE_USER" 
                expression="execution(public * my.service.package.ServiceImpl.*(..))"/>
    </security:global-method-security>
    

    定义服务实现bean:

    <bean id="rpcServiceTarget" class="my.service.package.ServiceImpl"/>
    

    为服务定义一个代理,并将其连接到拦截器:

    <bean id="rpcService" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces" value="my.service.package.Service"/>
        <property name="target" ref="rpcServiceTarget"/>
        <property name="interceptorNames">
            <list>
                <value>accessDeniedExceptionInterceptor</value>
            </list>
        </property>
    </bean>
    

    就是这样。如果您使用代理,您将获得从 Spring 的 AccessDeniedException 到您的自定义 GwtAccessDeniedException 的转换。

    【讨论】:

      猜你喜欢
      • 2017-06-14
      • 2013-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多