使用@RolesAllowed 和@PreAuthorize 保护控制器方法

Posted

技术标签:

【中文标题】使用@RolesAllowed 和@PreAuthorize 保护控制器方法【英文标题】:Securing controller method with @RolesAllowed and @PreAuthorize 【发布时间】:2015-01-16 08:34:02 【问题描述】:

我一直在努力解决这个问题有一段时间了。为了找到合适的解决方案,我已尽我所能,并遵循了很多 *** 示例和解决方案。

首先,我使用的是基于注释的解决方案。当我注释我的服务时,prePostEnabled 有效,但当我注释控制器时,它不起作用。此外,即使在我的服务上,jsr250Enabled 也不起作用。

通过将注释从安全配置移动到 MVC 配置,我发现很多案例已关闭,这在我的案例中不起作用。

我的设置如下所示:https://github.com/spring-projects/spring-security-oauth-javaconfig/tree/master/samples/oauth2-sparklr

但我使用的是 Servlet 3.0,并且我的 web.xml 中没有任何内容。

我的 SecurityInitializer 如下所示:

public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer 

我的 MVC 初始化程序如下所示:

public class MvcWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer 
@Override
protected Class<?>[] getRootConfigClasses() 
    return new Class<?>[]WebSecurityConfig.class, MethodSecurityConfig.class;


@Override
protected Class<?>[] getServletConfigClasses() 
    return new Class<?>[]SpringMvcConfig.class;


@Override
protected String[] getServletMappings() 
    return new String[]ApiPaths.API + "/*", "/res.jsp";

我的 WebSecurity 配置是这样初始化的:

@Configuration
@EnableWebSecurity
@ComponentScan(value = "com.roler.res.**.server", excludeFilters = 
    @Filter(type = FilterType.ASSIGNABLE_TYPE, value = SpringMvcConfig.class),
    @Filter(type = FilterType.ASSIGNABLE_TYPE, value = MethodSecurityConfig.class),
    @Filter(type = FilterType.REGEX, pattern = "com.xyz.*.controller.*"))
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 

我的 SpringMvcConfig 是这样初始化的:

@Configuration
@EnableWebMvc
@ComponentScan(value = "com.xyz.**.controller")
public class SpringMvcConfig extends WebMvcConfigurerAdapter 

如果你有任何想法,我已经没有果汁了,谢谢!

【问题讨论】:

【参考方案1】:

您描述的症状让我想到了代理问题。 Annotations 在服务层工作得很好,因为服务通常实现接口,Spring 可以很容易地使用 JDK 代理来放置 AOP 授权。

但控制器一般不实现接口。这就是为什么 PreAuthorize 注解更频繁地用于服务层的原因。恕我直言,您最好尝试使用基于 URL 模式的授权,而不是控制器上的 PreAuthorize 注释。另一种方法是使用带有 CGLIB 的目标类代理。

要使用 PreAuthorize 和 JSR-250 注释,您必须

用:注释你的spring安全配置类:

@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)

如果您在应用程序中的其他任何地方使用带有 JDK 代理的 Spring AOP,请让所有要在其中使用方法安全性的控制器类实现声明所有受保护方法的接口

如果您在应用程序的其他任何地方使用带有 CGLIB 代理的 Spring AOP,请将 proxyTargetClass = true 添加到 @EnableGlobalMethodSecurity

@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true,
        proxyTargetClass = true)

如果您想在 Spring 3.2 以下版本中使用 CGLIB 代理,请将 CGLIB 库添加到您的类路径中(CGLIB 类包含在 Spring 3.2+ 中)

避免混合 CGLIB 和 JDK 代理,因为 Spring 文档不建议这样做:多个部分在运行时折叠成一个统一的自动代理创建器,它应用任何部分(通常来自不同的 XML bean 定义文件)指定。这也适用于 and 元素。 需要明确的是:使用 'proxy-target-class="true"' on 或元素将强制对所有三个元素使用 CGLIB 代理。

但无论如何,我的建议是尝试将方法安全性转移到通常已经支持 AOP 的服务层。

【讨论】:

通过@EnableGlobalMethodSecurity,但我的首要任务仍然是在控制器上获得注释,因为我还有使用我的服务层的系统功能。 URL 模式也可以,但我还需要验证“内部”包含的一些信息,我想使用 AoP 在整个系统中无缝验证这些信息。 @ChristianGoudreau :使用配置精度编辑帖子。 好吧,为我的配置添加 proxyTargetClass 会使我的 ldap 配置失败,因为它不在 WebSecurityConfig 中。它说:无法将 org.springframework.security.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer@553d70ce 应用于已构建的对象 @ChristianGoudreau :这并不奇怪。这就是为什么我说如果您已经使用了 CGLIB 代理并且不要混合使用不同的代理模式,请使用 proxyTargetClass=true。您只剩下 2 个选项:将方法安全性迁移到服务层或让您的控制器实现接口以允许 JDK 代理。【参考方案2】:

我注意到了两件事(如 this thread 所述):

    注释中的 prePostEnabled 以启用 Pre/Post 注释 使用 CGLib 代理(Serge 也提到了这一点)

你的@EnableGlobalMethodSecurity 有这两个属性吗?

@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)

【讨论】:

以上是关于使用@RolesAllowed 和@PreAuthorize 保护控制器方法的主要内容,如果未能解决你的问题,请参考以下文章

使用 Enum 类型作为 @RolesAllowed-Annotation 的值参数

@RolesAllowed() 在 keycloak 中检查啥角色

Spring Security应用开发(20)基于方法的授权使用@RolesAllowed注解

在 SpringBoot 应用程序中使用 @RolesAllowed 的异常

对于使用 @RolesAllowed、@Secured 或 @PreAuthorize 的任何授权请求,Spring 启动授权返回 403

如何在 ContainerRequest 中获取会话对象以使用注解 @RolesAllowed(Role_user)?