如何在我的Controller中验证(@Valid)之前检查安全访问(@Secured或@PreAuthorize)?

时间:2014-04-01 08:35:33

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

这是我的控制器代码:

@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界面中我们可以找到这句话:

  

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

但似乎并非如此。

2 个答案:

答案 0 :(得分:2)

您不能直接执行此操作,因为{<1}}在实际方法调用之前处理,因此之前 @Valid处理

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

示例:

BindingResult

答案 1 :(得分:0)

如前所述,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. 然后HandlerInterceptor s
  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仍会参与授权的请求,表面上将是大多数请求。