See sample code in branch (feature/secured-interface-bug) to reproduce issue:
https://github.com/caspianb/SpringBootTest/tree/feature/secured-interface-bug
I reproduced this issue in Spring Boot 2.1.0, 2.1.4, and 2.1.8.
Spring Controllers seem to be getting confused when implementing an interface and adding @Secured to an @Override method:
@RestController
public class UserController implements BaseController {
@Override
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<String> getData() {
return ResponseEntity.ok("You have access! <a href=\"/api\">Protected Access?</a> | <a href=\"/logout\">Logout</a>");
}
@Override
@Secured("ROLE_ADMIN")
@RequestMapping(path = "api", method = RequestMethod.GET)
public ResponseEntity<String> getProtectedData() {
return ResponseEntity.ok("You have protected access! <a href=\"/logout\">Logout</a>");
}
}
The above example will always throw a 404 on either endpoint. Simply removing either the @Secured annotation or the implements BaseController will eliminate the 404.
The same occurs if I define the mappings on the interface itself. Ideally, I would like to define the mappings on the interface and then explicitly mark @Secured on implementing classes, but it seems odd that simply implementing an interface in conjunction with @Secured breaks the entire controller.
Note that trying to utilize either @RolesAllowed or@PreAuthorize has the same outcome as @Secured as described above.
This looks like it's either a Spring Security bug or an issue in Spring Framework. I'll transfer the issue to Spring Framework to see if they've seen it before.
I think this is due to proxying. The use of @Secured and @EnableGlobalMethodSecurity combined with implementing an interface is resulting in a JDK proxy being created for UserController. Such proxies lose the request mapping annotations. @EnableGlobalMethodSecurity(proxyTargetClass=true) should avoid the problem.
@caspianb You can learn more in Framework's reference documentation.
Thanks for the analysis, @wilkinsona.
@caspianb, please try out Andy's recommendation to use @EnableGlobalMethodSecurity(proxyTargetClass = true) and let us know if that resolves the issue for you.
Yup, forcing Spring to utilize the CGLIB proxies does resolve the 404 issue; thank you!
Thanks for confirming that it works!
FYI: I also improved the documentation by providing a Java config example.
I'll close this issue.
Most helpful comment
I think this is due to proxying. The use of
@Securedand@EnableGlobalMethodSecuritycombined with implementing an interface is resulting in a JDK proxy being created forUserController. Such proxies lose the request mapping annotations.@EnableGlobalMethodSecurity(proxyTargetClass=true)should avoid the problem.@caspianb You can learn more in Framework's reference documentation.