Spring Security: Deny access to controller methods, if @PreAuthorize annotation is missing
I'm answering my own question here.
I've solved the problem by using a HandlerInterceptorAdapter.
I'm not sure it is the most Spring-idiomatic way to achieve the result, but it's good enough for me.
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;
}
}
And in the Spring config:
<mvc:interceptors>
<beans:ref bean="mvcPreAuthorizeAnnotationCheckerInterceptor"/>
</mvc:interceptors>
<beans:bean id="mvcPreAuthorizeAnnotationCheckerInterceptor" class="com.acme.MvcPreAuthorizeAnnotationCheckerInterceptor"/>
I came up with a similar approach, but its not executed for every request but just extends the ConfigAttribute
's for the method:
A minor drawback could be that it does not allow for easy logging or whatever the big benefit is that it follows the same deny behavior as other not allowed endpoints.
SecurityConfig:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
return new CustomPermissionAllowedMethodSecurityMetadataSource();
}
}
MetadataSource:
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;
}
}
I also wrote a small article about this, with some further background: https://www.baeldung.com/spring-deny-access