Spring 安全性:如果缺少 @PreAuthorize 注释,则拒绝访问控制器方法

Spring Security: Deny access to controller methods, if @PreAuthorize annotation is missing

我有一个 Web 应用程序配置为以标准方式使用 Spring Security 3.2。

我正在使用 @PreAuthorize 注释来保护 Controllers 方法。 现在,我想 deny 访问每个控制器方法 UNLESS 它被注释为 @PreAuthorize.

我尝试了以下方法:

超级控制器

每个控制器都从注释为 @PreAutorize("denyAll") 的超级控制器扩展而来。这种方法似乎不起作用,因为控制器的方法注释被忽略了。万物皆禁

@PreAutorize("denyAll") 
public class SuperController {

}

public class MyController extends SuperController {

    @PreAuthorize("hasRole('SUPERHERO')")
    @RequestMapping(value = URL_PREFIX + "Add.do", method =  RequestMethod.GET)
    public String doStuff(Model model) {

        ...
    }

}

aop

在全局方法安全标记中使用切入点表达式

<global-method-security pre-post-annotations="enabled">
    <protect-pointcut expression="execution(* com.acme.*Controller.*(..))" access="denyAll" />
 </global-method-security>

这种方法也失败了:未注释的控制器方法仍然可以访问。

我在这里回答我自己的问题。

我已经使用 HandlerInterceptorAdapter.

解决了这个问题

我不确定这是实现结果的最 Spring 惯用方法,但对我来说已经足够了。

public class MvcPreAuthorizeAnnotationCheckerInterceptor extends HandlerInterceptorAdapter {
    final HandlerMethod hm;
    if (handler instanceof HandlerMethod) {
        hm = (HandlerMethod) handler;
        PreAuthorize annotation = hm.getMethodAnnotation(PreAuthorize.class);
        if (annotation == null) {
            // check if the class is annotated...
            annotation = hm.getMethod().getDeclaringClass().getAnnotation(PreAuthorize.class);
            if (annotation == null) {
                // add logging
                // or send a NON AUTHORIZED
                response.sendRedirect(request.getContextPath());
            }
       }
       return true;
    }
}

并且在 Spring 配置中:

<mvc:interceptors>
    <beans:ref bean="mvcPreAuthorizeAnnotationCheckerInterceptor"/>
</mvc:interceptors>

<beans:bean id="mvcPreAuthorizeAnnotationCheckerInterceptor" class="com.acme.MvcPreAuthorizeAnnotationCheckerInterceptor"/>

我想出了一个类似的方法,但它并没有针对每个请求执行,只是扩展了 ConfigAttribute 的方法:

一个小缺点可能是它不允许简单的日志记录,或者最大的好处是它遵循与其他不允许的端点相同的拒绝行为。

安全配置:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {

    @Override
    protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
        return new CustomPermissionAllowedMethodSecurityMetadataSource();
    }
}

元数据源:

public class CustomPermissionAllowedMethodSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource {
    @Override
    protected Collection<ConfigAttribute> findAttributes(Class<?> clazz) {
        return null;
    }

    @Override
    protected Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass) {
        Annotation[] annotations = AnnotationUtils.getAnnotations(method);
        List<ConfigAttribute> attributes = new ArrayList<>();

        // if the class itself is annotated as a @Controller we should by default deny access to every method
        if (AnnotationUtils.findAnnotation(targetClass, Controller.class) != null) {
            attributes.add(DENY_ALL_ATTRIBUTE);
        }

        if (annotations != null) {
            for (Annotation a : annotations) {
                // but not if the method has at least a PreAuthorize or PostAuthorize annotation
                if (a instanceof PreAuthorize || a instanceof PostAuthorize) {
                    return null;
                }
            }
        }
        return attributes;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }
}

我也写了一篇关于这个的小文章,有一些进一步的背景:https://www.baeldung.com/spring-deny-access