When you annotating method A with @Pre/PostAuthorize than spring will create proxy for such object and each call to such method will go through proxy (if object is under spring context).
The proxy for @Pre/PostAuthorize will evaluate expression from annotation (complicated here) and if it's true than pass the request father (to real method) if not than throw AccessDaniedException. You can also throw such exception by yourself in your method A and the effect will be the same. It's all about how @Pre/PostAuthorize works no more magic here!
But it's not the end of the story!
If AccessDaniedException is not suppressed or retranslated (without stack which doesn't have AccessDaniedException) than it will be catched by ExceptionTranslationFilter in Spring Security and ExceptionTranslationFilter will see if user is Authenticated
if yes than ExceptionTranslationFilter will delegate to AccessDeniedHandler to deal with the situation and default implementation of AccessDeniedHandler will return 403 http code or redirect to error page depends if you are using rest or not.
if not, than ExceptionTranslationFilter will delegate to AuthenticationEntryPoint and it will deal with the situation (redirect to login page, ask for http basic ... etc).
Note: ExceptionTranslationFilter will rethrow exception if any other than AuthenticationException or AccessDeniedException and probably 500 will be shown :(
Your problem is probably another problem look at Spring Security anonymous 401 instead of 403
Suppressed :(:
@GetMapping(value = "/test/ok")
public String getOk() {
try {
myService.securedMethod();
return "ok";
} catch (Exception e) {
log.info("AccessDeniedException suppressed not rethrowing :(", e);
return "error";
}
}
Correct retranslated :) :
@GetMapping(value = "/test/ok")
public String getOk() {
try {
myService.securedMethod();
return "ok";
} catch (AccessDeniedException e) {
throw new MyException(e); //public MyException(Throwable cause)
}
}