【问题标题】:How to check security acess (@Secured or @PreAuthorize) before validation (@Valid) in my Controller?如何在我的控制器中验证(@Valid)之前检查安全访问(@Secured 或 @PreAuthorize)?
【发布时间】:2014-05-11 22:03:35
【问题描述】:

这是我的控制器代码:

@PreAuthorize("hasRole('CREATE_USER')")
@RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public UserReturnRO createUser(@Valid @RequestBody UserRO userRO) throws BadParameterException{

    return userService.createUser(userRO);
}

我的需要是当没有适当角色的客户端尝试创建用户时,即使发送的数据无效,控制器也会响应“未授权”。取而代之的是,如果客户端(没有适当的角色)尝试创建具有错误数据的用户,我的控制器会以 @Valid 消息响应(例如:“密码不能为空”),而我希望它响应“未授权” .

PreAuthorized界面我们可以找到这句话:

用于指定方法访问控制表达式的注释,该表达式将被评估以确定是否允许方法调用。

但好像不是这样的。

【问题讨论】:

  • 验证错误也发生在方法之外 - 它们由 Valid 注释触发 - 就像 PreAuthorize 一样 - 在输入方法之前进行评估。我不确定是否可以在那里更改订单?我的猜测是——不。为什么在这两种情况下都需要 403?
  • 因为我不希望未经授权的用户访问验证规则
  • 好的。我想知道如果你把它放在验证器上@PreAuthorize 是否会起作用?
  • Spring 团队已经知道这个问题github.com/spring-projects/spring-boot/issues/10157

标签: spring validation spring-mvc spring-security controller


【解决方案1】:

您不能直接执行此操作,因为@Valid实际方法调用之前被处理,结果@PreAuthorize

但是您可以做的是在您的模型 (userRO) 之后立即注入 BindingResult 并这样做 - 控制验证过程。然后检查BindingResult是否有错误,如果有则返回错误的请求响应(类似于spring所做的)。

例子:

@ResponseBody
@RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasRole('CREATE_USER')")
public ResponseEntity<?> createUser(@RequestBody @Valid UserRO userRO, BindingResult result) {
    if (result.hasErrors()) {
        return ResponseEntity.badRequest().body(result.getAllErrors());
    }
    return ResponseEntity.ok(userService.createUser(userRO));
}

【讨论】:

  • 工作起来就像一个魅力,但这种行为绝对是奇怪的(他们不是说“安全第一”吗?)。
【解决方案2】:

如前所述,Spring Security 的 @PreAuthorize 是方法建议,这意味着在方法及其参数已经解决之前它不会参与。

除了the answer already given,还有一些方法可以在参数解析之前移动授权。

过滤安全

首先,Spring Security 在将请求映射到方法之前检查 URL。由于这是@Controller,因此可以合理地假设您可以将请求映射到该级别的角色而不是@PreAuthorize

http
    .authorizeRequests()
        .mvcMatchers(POST, "/somepath").hasRole("CREATE_USER")

处理程序拦截器

其次,Spring MVC 在解析方法参数之前确实提供了对检查权限的有限支持。例如,您可以这样做:

@EnableWebMvc
public static class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        UserRoleAuthorizationInterceptor userRole =
            new UserRoleAuthorizationInterceptor();
        userRole.setAuthorizedRoles("CREATE_USER");
        registry.addInterceptor(userRole);
    }
}

这比 @PreAuthorize 基本得多,因为它是一个全局设置,但为了完整起见,我将其包含在内。

处理程序拦截器,第 2 部分

第三(警告,前面有些不雅),您可以创建自己的HandlerInterceptor

流程是:

  1. FilterSecurityInterceptor .mvcMatchers(...).hasRole(...) 生活在哪里
  2. 然后HandlerInterceptors
  3. 然后是参数验证
  4. 然后MethodSecurityInterceptor @PreAuthorize 生活的地方

因此,您的HandlerInterceptor 会在解决参数之前进行检查。不过,它不必像MethodSecurityInterceptor 那样复杂。例如,它可能只是:

static class AuthorizationInterceptor extends HandlerInterceptorAdapter {
    SecurityMetadataSource securityMetadataSource;
    AccessDecisionManager accessDecisionManager;

    @Override
    public void preHandle(HttpServletRequest request,
        HttpServletResponse response, Object handler) {

        Authentication authenticated = (Authentication) request.getUserPrincipal();
        MethodInvocation mi = convert(handler);
        Collection<ConfigAttribute> attributes =
            this.securityMetadataSource.getAttributes(mi);

        // throws AccessDeniedException
        this.accessDecisionManager.decide(authenticated, mi, attributes);
        return true;
    }
}

然后你把它连接在一起:

@EnableGlobalMethodSecurity(prePostEnabled = true)
static class MethodConfig extends GlobalMethodSecurityConfiguration {
    @Bean
    HandlerInterceptor preAuthorize() throws Exception {
        return new AuthorizationInterceptor(
            accessDecisionManager(), methodSecurityMetadataSource());
    }
}

@EnableWebMvc
public static class MvcConfig implements WebMvcConfigurer {
    @Autowired
    AuthorizationInterceptor authorizationInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authorizationInterceptor);
    }
}

这很不雅,因为MethodSecurityInterceptor 仍会参与授权请求,而这表面上是大多数。

【讨论】:

    猜你喜欢
    • 2022-01-02
    • 2013-01-02
    • 2013-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多