【问题标题】:How to make @PreAuthorize having higher precedence than @Valid or @Validated如何使@PreAuthorize 具有比@Valid 或@Validated 更高的优先级
【发布时间】:2015-05-09 12:12:24
【问题描述】:

我使用的是spring boot,我已经在WebSecurityConfigurerAdapter中启用了全局方法安全性

@EnableGlobalMethodSecurity(prePostEnabled = true, order = Ordered.HIGHEST_PRECEDENCE) 

下面是我的控制器代码

@PreAuthorize("hasAnyRole('admin') or principal.id == id")
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public User updateUser(@PathVariable("id") String id,  @Valid @RequestBody   UserDto userDto) 
{ ....}

但是,当非管理员用户尝试执行 PUT 请求时,JSR303 验证器将在 @PreAuthorize 之前启动。 例如,非管理员用户最终会得到类似“需要名字”而不是“拒绝访问”的内容。但是在用户提供名字变量以通过验证器后,访问被拒绝。

有谁知道如何在@Valid 或@Validated 之前强制检查@PreAuthorize?

为了执行一些复杂的规则检查,我必须使用这种方法级别的授权而不是基于 url 的授权。

【问题讨论】:

  • 这永远不会发生。 @PreAuthorize 仅在方法执行时被调用。但是,@Valid 在准备方法执行时进行处理,这发生在方法的实际执行之前。所以这行不通。作为一种解决方法,您可以进行手动验证,而不是依赖 @Valid 注释。
  • 我明白了。感谢您解释它是如何在后台工作的。我认为如果他们将来能够支持 PreAuthorize 之后的验证,那就太好了。
  • 这会很困难,因为这会改变 AOP 的整个工作方式:)。我可以看到映射 url 和增加安全性的改进。目前只使用了 ant 样式表达式和正则表达式,但是我可以看到人们可能还想使用路径变量来保证安全。

标签: spring-mvc spring-security controller bean-validation role-base-authorization


【解决方案1】:

我遇到了同样的问题,我找到了这篇文章。 M. Deinum 的评论帮助我了解出了什么问题

这就是我所做的:

  1. 公共方法有@PreAuthorize 并进行检查
  2. @RequestBody 参数上有NO@Valid
  3. 我创建了第二个方法,私有,我在其中执行 DTO 验证。使用@Valid 注解
  4. 公共方法将调用委托给私有方法。只有公有方法被授权,才会调用私有方法

例子:

@RequestMapping(method = RequestMethod.POST)
@PreAuthorize("hasRole('MY_ROLE')")
public ResponseEntity createNewMessage(@RequestBody CreateMessageDTO createMessageDTO) {
    // The user is authorized
    return createNewMessageWithValidation(createMessageDTO);
}

private ResponseEntity createNewMessageWithValidation(@Valid CreateMessageDTO createMessageDTO) {
   // The DTO is valid
   return ...
}

【讨论】:

  • 有什么干净的解决办法吗?
【解决方案2】:

对于相同的场景,我发现了通过弹簧过滤器实现安全性的建议。
这是类似的帖子:How to check security acess (@Secured or @PreAuthorize) before validation (@Valid) in my Controller?

另外,也许是一种不同的方法 - 尝试通过在 @InitBinder 中注册自定义验证器来使用验证(因此跳过 @valid 注释)。

访问过滤器类中的主体对象:

  SecurityContextImpl sci = (SecurityContextImpl)     
session().getAttribute("SPRING_SECURITY_CONTEXT");

if (sci != null) {
    UserDetails cud = (UserDetails) sci.getAuthentication().getPrincipal();

 }

在这种情况下,/{id} 是 URL 中的路径参数。访问过滤器或拦截器类中的路径参数:

String[] requestMappingParams =    ((HandlerMethod)handler).getMethodAnnotation(RequestMapping.class).params()

        for (String value : requestMappingParams) {.

【讨论】:

  • 谢谢,我也看到了那篇文章,但是 spring 过滤器(基于 url)在我的情况下不起作用,因为我需要使用 url 参数验证主体 ID。我在我的实现中使用了@InitBinder,但是@PreAuthorize 在验证器之前仍然没有被调用。
  • 您可以访问 servlet 过滤器中的主体对象。这有帮助吗?
  • 我在你的 servlet 过滤器类中添加了一个访问主要对象的 sn-p..我不确定这是否会让你更容易......
  • 谢谢,我以前从未使用过 servlet fitler。因此,假设我的 REST PUT 请求之一是 /user/{id} 并且我只允许用户编辑他们自己的帐户。如何检查过滤器中的 {id}?例如,用户 id =2 只允许在 /user/2 而不是 /user/3 上发出 PUT 请求?
  • 哦,我明白了。好的,我添加了一些代码来显示如何从请求对象访问路径参数/{id}。我认为这会奏效。希望对您有所帮助!
【解决方案3】:

使用WebSecurityConfigurerAdapter.configure(HttpSecurity http) 而不是@PreAuthorize

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
  @Override
  protected void configure(HttpSecurity http) throws    Exception {
    http
      .authorizeRequests()
      .mvcMatchers( "/path/**").hasRole("admin");
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-02
    • 2021-12-09
    • 1970-01-01
    • 2017-02-20
    • 1970-01-01
    • 2021-04-16
    • 1970-01-01
    相关资源
    最近更新 更多