Careeroza
Real World Example
Order Ownership at Swiggy-Style Apps
In a food or e-commerce app, the most common security bug is IDOR (Insecure Direct Object Reference): changing /orders/1001 to /orders/1002 and seeing a stranger's address and phone number. Every app like Swiggy, Zomato or Flipkart protects order, address and payment endpoints with an ownership check. In Spring, that check is method security.
@Service
public class OrderService {
private final OrderRepository orders;
public OrderService(OrderRepository orders) { this.orders = orders; }
// customers: only their own order; support staff: any order
@PreAuthorize("hasAuthority('order:read:any') or @orderSecurity.isOwner(#orderId, authentication)")
public OrderDto details(Long orderId) {
return orders.findById(orderId).map(OrderDto::from)
.orElseThrow(() -> new OrderNotFoundException(orderId));
}
// refunds above Rs 5000 need the finance permission
@PreAuthorize("hasAuthority('order:refund') and (#amount <= 5000 or hasAuthority('refund:large'))")
public RefundDto refund(Long orderId, BigDecimal amount) {
// ... call payment gateway, save refund
}
// list endpoint: filter in the query, not with @PostFilter
@PreAuthorize("isAuthenticated()")
public Page<OrderDto> myOrders(Authentication auth, Pageable pageable) {
return orders.findByCustomerEmail(auth.getName(), pageable).map(OrderDto::from);
}
}Result
Example
A support agent with order:refund (but not refund:large) refunds Rs 1,200, then tries Rs 25,000:
curl -i -X POST -u [email protected]:pass -H "Content-Type: application/json" \
-d '{"amount":1200}' localhost:8080/api/orders/1001/refund
curl -i -X POST -u [email protected]:pass -H "Content-Type: application/json" \
-d '{"amount":25000}' localhost:8080/api/orders/1003/refundExample output
HTTP/1.1 200 OK
{"orderId":1001,"amount":1200.0,"status":"REFUND_INITIATED"}
HTTP/1.1 403 Forbidden