How to make @PreAuthorize with higher priority than @Valid or @Validated

I am using spring boot and I have enabled global method protection in the WebSecurityConfigurerAdapter on

@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-administrator user tries to execute a PUT request, the JSR303 validator will exit before @PreAuthorize. For example, a non-administrator user would get something like "name required" instead of "access denied". But after the user provided the first name variable to pass the validator, access was returned.

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

And I have to use this kind of authorization at the method level instead of URL-based authorization in order to do some complicated rule checking.

+9
source share
3 answers

In the same scenario, I found security recommendations with spring filters.
Here is a similar article: How to check the security of acess (@Secured or @PreAuthorize) before checking (@Valid) in my controller?

Also, maybe a different approach - try using validation by registering a custom validator with @InitBinder (so skip the @valid annotation).

To access the main object in the filter class:

  SecurityContextImpl sci = (SecurityContextImpl) session().getAttribute("SPRING_SECURITY_CONTEXT"); if (sci != null) { UserDetails cud = (UserDetails) sci.getAuthentication().getPrincipal(); } 

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

 String[] requestMappingParams = ((HandlerMethod)handler).getMethodAnnotation(RequestMapping.class).params() for (String value : requestMappingParams) {. 
0
source

I had the same problem and I found this post. Comment by M. Deinum helps me understand what was wrong.

Here is what I did:

  • The public method has @PreAuthorize and performs validation
  • @RequestBody parameter missing @Valid value
  • I create a second private method where I do a DTO check. Using @Valid annotation
  • Public methods delegate the call to the private. The private method is called only the allowed public method.

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
source

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"); } } 
0
source

Source: https://habr.com/ru/post/983492/


All Articles