【问题标题】:Can Spring Security use @PreAuthorize on Spring controllers methods?Spring Security 可以在 Spring 控制器方法上使用 @PreAuthorize 吗?
【发布时间】:2011-03-06 11:28:23
【问题描述】:

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

【问题讨论】:

    标签: java spring-mvc controller spring-security


    【解决方案1】:

    是的,它工作正常。

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

    【讨论】:

    • 我把它放在我的 spring 安全应用程序上下文中(实际上我已经有了它)但是 Spring 没有使用 @Controller 对控制器做任何事情。我是否需要做一些特别的事情才能让这个工作超出你所说的范围?在
    • 我说,global-method-security 应该在 DispatcherServlet 的上下文中 (...-servlet.xml) 而不是在“spring security application context”中。
    • 它们没有合并。 DispatcherServlet 的上下文是ContextLoaderListener 的子上下文。所以它们有不同的 AOP 配置,因此需要不同的 <global-method-security> 出现。
    • 谢谢,axtavt。你拯救了我的一天。 spring 文档没有提到 应该在 ...-servlet.xml 中。我也将它放在安全应用程序上下文中并且没有工作。一旦它移到 ...-servlet.xml,它就开始工作了。我还有一个问题,什么时候应该将 放在安全应用程序上下文中?
    • @Georgie:它应该放置在您声明要应用安全注释的 bean 的每个上下文中。
    【解决方案2】:

    Spring Security FAQ(强调我的)。

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

    通常我们建议在服务中应用方法安全性 层而不是单个 Web 控制器。

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

    【讨论】:

    • 试图在控制器中使用@PreAuthorize,没有工作,一旦我移动到服务层就工作了。
    【解决方案3】:

    如果你使用的是 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";
    }
    

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

    【讨论】:

      【解决方案4】:

      问这个问题已经两年多了,但由于我今天遇到的问题,我宁愿不鼓励在@Controllers 上使用@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) {
          // ...
      }
      

      Validator 根本不会触发(Spring MVC 4.1.2、Spring Security 3.2.5)并且不执行任何检查。

      类似的问题是由 Spring 使用的 CGLIB 代理引起的(当类没有实现接口时,Spring 会创建 CGLIB 代理;如果类实现了任何接口,则生成 JDK 代理 - documentationwell explained here 和 @ 987654323@)。

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

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

      <http use-expressions="true" disable-url-rewriting="true">
      
          ...
      
          <intercept-url pattern="/administration/**" access="hasRole('ROLE_ADMIN')" />
      
      </http>
      

      【讨论】:

        【解决方案5】:

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

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

        查看具体角色。

        【讨论】:

          【解决方案6】:

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

          @EnableGlobalMethodSecurity(prePostEnabled=true)
          

          【讨论】:

            【解决方案7】:

            首先你需要在你的WebSecurityConfig中添加这个注解来启用@Pre和@Post注解。

                @EnableGlobalMethodSecurity(prePostEnabled = true)
            

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

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

            相当于

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

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

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

            【讨论】:

            • 你确定它是等价的吗?我认为表达式 hasRole 会自动将 ROLE_ 前缀添加到提供的参数中。
            【解决方案8】:

            step1: 在 SecurityConfig 类中添加 @EnableGlobalMethodSecurity(prePostEnabled = true) 注释。 像这样:

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

            第 2 步:您可以在控制器或服务或存储库层中添加 @PreAuthorize() 注释。在方法或类级别。 例如:

            @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() {
               }
            }
            

            【讨论】:

              猜你喜欢
              • 2014-08-03
              • 2015-08-12
              • 1970-01-01
              • 1970-01-01
              • 2015-03-25
              • 1970-01-01
              • 2017-07-25
              • 2017-07-29
              相关资源
              最近更新 更多