How to make @PreAuthorize having higher precedence than @Valid or @Validated

不想你离开。 提交于 2020-01-01 08:35:12

问题


I am using spring boot, and I have enabled the global method security in WebSecurityConfigurerAdapter by

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

And Below is my controller code

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

However, when a non-admin user try to do a PUT request, the JSR303 validator will kick in before @PreAuthorize. For example, non-admin user ended up getting something like "first name is required" instead of "access denied". But after user supplied the first name variable to pass the validator, access denied was returned.

Does anyone know how to enforce the @PreAuthorize get checked before @Valid or @Validated?

And I have to use this kind of method-level authorization instead of url-based authorization in order to perform some complex rule checking.


回答1:


For the same scenario, I have found reccomendations to implement security via spring filters.
Here is similar post : How to check security acess (@Secured or @PreAuthorize) before validation (@Valid) in my Controller?

Also, maybe a different approach - try using validation via registering a custom validator in an @InitBinder (thus skip the @valid annotation).

To access principal object in filter class:

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

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

 }

In this case /{id} is a path param in the URL. To access path params in filter or interceptor class:

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

        for (String value : requestMappingParams) {.



回答2:


I had the same issue and I found this post. The comment of M. Deinum helps me to understand what was going wrong

Here is what I did :

  1. The public method has the @PreAuthorize and do the check
  2. There is NO @Valid on the @RequestBody parameter
  3. I create a second method, private, where I do the DTO validation. Using the @Valid annotation
  4. The public methods delegates the call to the private one. The private method is called only is the public method is authorized

Example :

@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 ...
}



回答3:


Use WebSecurityConfigurerAdapter.configure(HttpSecurity http) instead of @PreAuthorize

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


来源:https://stackoverflow.com/questions/28923360/how-to-make-preauthorize-having-higher-precedence-than-valid-or-validated

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!