Expected Behavior
The @ PreAuthorize annotation on the class and the @ PreAuthorize annotation on the methods below the class that they can collectively be thought of as being 'and' togethe,both are authorized before proceeding to the next step Current Behavior
When I use the @ PreAuthorize annotation as a permission judgment: when setting it on both the class and method, only the @ PreAuthorize annotation on the method takes effect,that is, only the @PreAuthorize authorization permission on the method is required, and the @PreAuthorize on the class is ineffective. after debugging, I found that the first step is to query whether there is a @ PreAuthorize annotation on the method. If so, it is determined based on the spel expression and whether the user has this permission; If not, check if there is a @ PreAuthorize annotation on the class before making a judgment,if any, perform corresponding permission judgment; what I mean is like being authorized like multiple method security annotations, for an invocation to be authorized, all annotation inspections need to pass authorization.
for example: when my identity is ROLE_USER and I have WRITE permission, I can pass the authentication when calling the /user1 interface, which is different from what I expected
@PreAuthorize("hasRole('ADMIN')")
public class UserController {
@Resource
RoleService roleService;
@PreAuthorize("hasAuthority('WRITE')")
@GetMapping("/user1")
public R<List<Role>> test1(){
List<Role> roles = roleService.getRolesByIdNo("007");
return R.makeOkResponse(roles);
}
}
Context
Comment From: Fliix-rgb
spring-boot-start-security:version 3.1.1 jdk17
Comment From: marcusdacoregio
Hi @Fliix-rgb, thanks for reaching out.
That behavior is expected as it is described in the documentation.
In your scenario, I'd say that one approach would be to add request security to your UserController and then fine-grained authorization for each of its methods, for example:
http.
authorizeHttpRequests(authorize -> authorize
.requestMatchers("/users/**").hasRole("ADMIN")
);
@RequestMapping("/users")
public class UserController {
@Resource
RoleService roleService;
@PreAuthorize("hasAuthority('WRITE')")
@GetMapping("/user1")
public R<List<Role>> test1(){
// ...
}
}
Or, you can combine both authorization checks into one expression or annotation.