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
我有一个 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