Reputation: 1127
I've played a bit with spring securities method security and got a very strange behavior. I have several controller classes and the methods are annotated with @PreAuthorize to restrict access for certain user roles.
There is one Controller Class who's injected objects are null after adding method security. I debugged into my code and found the following:
service and userService are the injected objects
@Controller
public class OrderController {
@Autowired
private OrderService service;
@Autowired
private UserService userService;
What seems strange to me is the value description of this: OrderController$$EnhancerBySpringCGLIB$$1a7122f6. By removing all MethodSecurity annotations the class works as expected.
When I look at other Controller Classes which also use method security then they work just fine and the variable list in the debugger looks ok:
@Controller
public class UserController {
@Autowired
private UserService service;
I also searched for errors I could have made with the annotations but the annotations in OrderController look the same as in the other Controller classes. Here examples of the OrderController Class:
@Controller
public class OrderController {
.
.
.
@GetMapping("/dispo/dispo")
@PreAuthorize("hasAuthority('ADMIN') or hasAuthority('DISPATCH')")
private String showDispoPage() {
return "/dispo/dispo";
}
@GetMapping("/dispo/orderCreate")
@PreAuthorize("hasAuthority('ADMIN') or hasAuthority('AL_SYNC_ADMIN') or hasAuthority('CLIENT_USER') or hasAuthority('DISPATCH')")
private String showCreateOrder(Model model) {
List<MdUser> userList = service.getUsers();
model.addAttribute("userList", userList);
return "/dispo/orderCreate";
}
}
And here an example of another Controller Class which workes as expected:
@Controller
public class UserController {
.
.
.
@GetMapping("/admin/user")
@PreAuthorize("hasAuthority('ADMIN') or hasAuthority('DISPATCH') or hasAuthority('WEBTOOL_USER')")
public String showInvalidUserPage(Model model) {
List<UserModel> invalidUserList = service.findInvalidUsers(service.getUsers());
model.addAttribute("userList", invalidUserList);
return "/admin/user";
}
@GetMapping("/admin/userCreate")
@PreAuthorize("hasAuthority('ADMIN')")
public String showNewUserPage(Model model) {
UserModel user = new UserModel();
model.addAttribute("user", user);
return "/admin/userCreate";
}
}
So what could possibly went wrong here? I don't understand why this one class has a different behavior than the other classes.
Upvotes: 8
Views: 912
Reputation: 637
For Kotlin users, make sure the method is not final!
Two days wasted.
Upvotes: 4
Reputation: 3430
Spring Security uses AOP (Aspect Oriented Programming) to "wrap" an annotated method with AOP advice. Once a method with a pre/post security annotation is invoked, the advice checks whether the user is authenticated and authorized. The method is invoked if it successfully verifies, otherwise some unauthorized/unauthenticed flow is traversed.
Spring AOP can only perform runtime weaving on public methods. The showDispoPage
and showCreateOrder
are both private, which might interfere with the security advice.
I would move the pre/post authorization annotations to the Service layer. Not only does it provide better separation between controller annotations and security annotations, it also safeguards against any future errors. For instance, in your current set up, any calls to the UserService
method will not be verified if they are being invoked through a different Controller where the security annotations have been forgotten.
Furthermore, you may also choose to use web security
(securing access to URIs, such as securing /some/path
) along with the current method level security
set up.
Upvotes: 8