============================================================================== Demo 6 -- interceptor order, and @PostAuthorize vs @Transactional ============================================================================== AuthorizationInterceptorsOrder, read from the enum itself --------------------------------------------------------- CONSTANT getOrder() FIRST -2147483648 PRE_FILTER 100 PRE_AUTHORIZE 200 SECURED 300 JSR250 400 SECURE_RESULT 450 POST_AUTHORIZE 500 POST_FILTER 600 LAST 2147483647 Lower order = higher precedence = further OUT in the chain. Spring's own @Transactional advisor defaults to Ordered.LOWEST_PRECEDENCE (2147483647), which is larger than every number above -- so security wraps transactions, not the other way round. SLF4J(W): No SLF4J providers were found. SLF4J(W): Defaulting to no-operation (NOP) logger implementation SLF4J(W): See https://www.slf4j.org/codes.html#noProviders for further details. The advisor chain on a bean carrying all four annotations --------------------------------------------------------- advisors applied to the proxy: 4 ORDER ADVISOR BEAN (as registered by @EnableMethodSecurity) 100 preFilterAuthorizationMethodInterceptor 200 preAuthorizeAuthorizationMethodInterceptor 450 authorizeReturnObjectMethodInterceptor 500 postAuthorizeAuthorizationMethodInterceptor 600 postFilterAuthorizationMethodInterceptor (authorizeReturnObject sits at SECURE_RESULT = 450 and is registered whether or not anything in the app uses @AuthorizeReturnObject.) For BEFORE advice, a lower order runs earlier: @PreFilter (100) really does run before @PreAuthorize (200). For AFTER advice the same numbers mean the opposite. @PostAuthorize (500) sits FURTHER OUT than @PostFilter (600), so on the way back out @PostFilter finishes first and @PostAuthorize evaluates returnObject on the ALREADY-FILTERED list. Both methods below return the same 3 elements and filter one away: @PostAuthorize returnObject.size() == 3 DENIED -> AuthorizationDeniedException: Access Denied @PostAuthorize returnObject.size() == 2 ALLOWED -> [a, b] Default order: @PostAuthorize denies AFTER the transaction commits ------------------------------------------------------------------ rows before : 0 recordAndReturn("bob") @PostAuthorize DENIED -> AuthorizationDeniedException: Access Denied rows after the denial : 1 @EnableTransactionManagement(order = FIRST): the write rolls back ----------------------------------------------------------------- rows before : 0 recordAndReturn("bob") @PostAuthorize DENIED -> AuthorizationDeniedException: Access Denied rows after the denial : 0