Careeroza
@PostAuthorize and Filtering
@PostAuthorize: Checking the Result
Sometimes you only know if the user may see something after loading it. @PostAuthorize runs after the method and can read the result as returnObject.
@Service
public class OrderService {
@PostAuthorize("hasRole('ADMIN') or returnObject.customerEmail == authentication.name")
public OrderDto findById(Long orderId) {
return orderRepository.findById(orderId)
.map(OrderDto::from)
.orElseThrow(() -> new OrderNotFoundException(orderId));
}
}Note: The method still runs, so never use @PostAuthorize on methods that change data (the change happens before the check). It is for reads only.
@PreFilter and @PostFilter
@PostFilter removes items from a returned collection; filterObject is each element. @PreFilter does the same with a collection argument.
@PostFilter("hasRole('ADMIN') or filterObject.customerEmail == authentication.name")
public List<OrderDto> recentOrders() {
return orderRepository.findTop50ByOrderByCreatedAtDesc().stream().map(OrderDto::from).toList();
}Tip: Filtering happens in memory after the query. For big tables, filter in the query instead (findByCustomerEmail(authentication.getName())). Use @PostFilter only for small lists.
Request and Response
Example
Asha opens her own order and then someone else's:
curl -i -u [email protected]:asha123 localhost:8080/api/orders/1001
curl -i -u [email protected]:asha123 localhost:8080/api/orders/1002Example output
HTTP/1.1 200 OK
{"id":1001,"customerEmail":"[email protected]","status":"SHIPPED","total":2998.0}
HTTP/1.1 403 Forbidden