@PreAuthorize and SpEL

Checking Before the Method Runs

@PreAuthorize takes a SpEL (Spring Expression Language) expression. It runs before the method. You can use the same functions as URL rules plus the method arguments with #name.

@Service
public class ProductService {

    @PreAuthorize("hasAnyRole('SELLER', 'ADMIN')")
    public ProductDto create(CreateProductRequest req) { ... }

    @PreAuthorize("hasRole('ADMIN')")
    public void delete(Long productId) { ... }

    // the seller may only change their own products; admins may change any
    @PreAuthorize("hasRole('ADMIN') or #sellerEmail == authentication.name")
    public ProductDto updatePrice(Long productId, String sellerEmail, BigDecimal price) { ... }
}

Useful SpEL Building Blocks

ExpressionMeaning
hasRole('ADMIN'), hasAnyRole(...)Role checks (ROLE_ prefix added)
hasAuthority('order:refund')Exact authority
isAuthenticated(), isAnonymous()Logged in or not
authenticationThe current Authentication object
authentication.nameUsername (email in ShopEase)
principalThe principal, e.g. your UserDetails
#orderIdMethod parameter named orderId
@orderSecurity.isOwner(#id, authentication)Call a Spring bean named orderSecurity
and, or, notCombine conditions

Parameter Names

Tip: #orderId works because Spring Boot's Maven/Gradle setup compiles with the -parameters flag, so parameter names are kept. If you build without it, Spring cannot find #orderId; add -parameters or annotate the parameter with @P("orderId").

Request and Response

Example

Ravi (seller) changes the price of his own product, then tries someone else's:

curl -i -X PATCH -u [email protected]:ravi123 -H "Content-Type: application/json" \
  -d '{"price": 1399}' localhost:8080/api/seller/products/7/price
curl -i -X PATCH -u [email protected]:ravi123 -H "Content-Type: application/json" \
  -d '{"price": 99}' localhost:8080/api/seller/products/31/price

Example output

HTTP/1.1 200 OK
{"id":7,"name":"boAt Rockerz 450","price":1399.0,"seller":"[email protected]"}

HTTP/1.1 403 Forbidden