Spring 安全性可以在 Spring 控制器方法上使用@PreAuthorize 吗?

Spring 安全性可以在 Spring 控制器方法上使用 @PreAuthorize吗?

55041 次浏览

是的,很好用。

你需要 ...-servlet.xml中的 <security:global-method-security pre-post-annotations="enabled" />。它还需要 CGLIB 代理,所以要么您的控制器不应该有接口,要么您应该使用 proxy-target-class = true

参见 Spring 安全常见问题(强调我的)。

在 Springweb 应用程序中,包含 调度程序 servlet 的 Spring MVC bean 通常与 主应用程序上下文。它通常定义在一个名为 Myapp-servlet.xml,其中“ myapp”是分配给 Spring 的名称 一个应用程序可以有多个 DispatcherServlets,每个都有自己独立的应用程序上下文。 这些“子”上下文中的 bean 对于 “父”应用程序上下文由 在 web.xml 中定义的 ContextLoaderListener 对所有用户都是可见的 这个父上下文通常是你定义 您的安全配置,包括 元素中的方法应用的任何安全约束 这些 web bean 不会被强制执行,因为 bean 不能被看到 从 DispatcherServlet 上下文中移除 声明到网络上下文或移动 您希望保护到主应用程序上下文中的 bean。

通常,我们建议在服务中应用方法安全性 层而不是单独的 web 控制器

如果您将切入点应用到服务层,那么您只需要在应用程序的安全上下文中设置 <global-method-security>

如果你使用的是 Spring 3.1,你可以用它做一些很酷的事情。看看 https://github.com/mohchi/spring-security-request-mapping。这是一个将@PreAuthorize 与 Spring MVC 的 RequestMappingHandlerMapping 集成在一起的示例项目,您可以这样做:

@RequestMapping("/")
@PreAuthorize("isAuthenticated()")
public String authenticatedHomePage() {
return "authenticatedHomePage";
}


@RequestMapping("/")
public String homePage() {
return "homePage";
}

如果用户通过了身份验证,那么对“/”的请求将调用 enticatedHomePage () ,否则将调用 homePage ()。

这个问题已经问了两年多了,但是由于我今天遇到的问题,我宁愿不鼓励在 @Controller上使用 @Secured@PreAuthorize等等。

@Validated@Secured控制器结合在一起对我不起作用:

@Controller
@Secured("ROLE_ADMIN")
public class AdministrationController {


// @InitBinder here...


@RequestMapping(value = "/administration/add-product", method = RequestMethod.POST)
public String addProductPost(@ModelAttribute("product") @Validated ProductDto product, BindingResult bindingResult) {
// ...
}

验证器根本不会触发(SpringMVC 4.1.2,SpringSecurity 3.2.5) ,也不会执行任何检查。

类似的问题也是由 Spring 使用的 CGLIB 代理造成的(当没有类实现的接口时,Spring 会创建 CGLIB 代理; 如果类实现了任何接口,那么就会生成 JDK 代理—— 文件解释得很好给你)。

正如我在上面链接的答案中所提到的,在通常实现接口(因此使用 JDK 代理)的服务层上使用 Spring Security 注释并不会更好,因为这不会导致这样的问题。

如果你想保护 web 控制器,更好的办法是使用 <http><intercept-url />,它们绑定到特定的 URL,而不是控制器中的方法,并且工作得很好。就我而言:

<http use-expressions="true" disable-url-rewriting="true">


...


<intercept-url pattern="/administration/**" access="hasRole('ROLE_ADMIN')" />


</http>

要扩展 Andy 提供的答案,可以使用:

@PreAuthorize("hasRole('foo')")

检查具体的角色。

关于如何通过更改 xml 配置使其工作,已经有了答复; 但是,如果您使用的是基于代码的配置,则可以通过在 @Configuration类上放置以下注释来实现同样的功能:

@EnableGlobalMethodSecurity(prePostEnabled=true)

首先,需要在 WebSecurityConfig 中添加这个注释,以启用@Pre 和@Post 注释。

    @EnableGlobalMethodSecurity(prePostEnabled = true)

您还可以按照以下方式检查角色/权限

    @PreAuthorize("hasAuthority('ROLE_ADMIN')")

等同于

    @PreAuthorize("hasRole('ROLE_ADMIN')")

您还可以检查多个角色/权限,如下所示

    @PreAuthorize("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_USER') or ...")

Step 1: 在 SecurityConfig 类中添加 @ EnableGlobalMethodSecurity (prePostEnable = true)注释。 像这样:

 @Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
.....
}

步骤2: 您可以在控制器、服务或存储库层添加 @ 预先授权()注释。 例如:

@RestController
@PreAuthorize("isAuthenticated()")
public class WebController {
  

@PreAuthorize("permitAll()")
@GetMapping("/")
public String home() {
return "Welcome home!";
}


@GetMapping("/restricted")
public String restricted() {
return "restricted method";
}
}

或者

@RestController
public class AdminController {
   

   

@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin")
public String adminMethod() {
}
}