Owner-only rules with a guard bean

Spring Security · lesson 25 of 31 · 3 min read

Express "only the owner may edit this row" without copying the check into every controller method.

Open this lesson in the learning hub

Key points

  • Roles cannot answer "is this row yours". That needs the object, so the check belongs next to the data.
  • Call a bean from SpEL: @PreAuthorize("@postGuard.canEdit(#postId, authentication)") runs an ordinary query.
  • Prefer @PreAuthorize with an id to @PostAuthorize on the result - a post-check does the work, then throws it away.
  • Filtering a list is a query concern. Add where owner_id = ? instead of loading everything and filtering in Java.
  • A failed check throws AccessDeniedException, so it lands on exactly the same 403 path as a URL rule.
  • Spring Security ACLs exist for per-row grants, but a small guard bean covers ownership with far less machinery.

Example

@Component("postGuard")
public class PostGuard {

    private final PostRepository posts;

    public boolean canEdit(Long postId, Authentication auth) {
        boolean admin = auth.getAuthorities().stream()
            .anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));

        return admin || posts.findById(postId)
            .map(p -> p.getAuthorEmail().equals(auth.getName()))
            .orElse(false);
    }
}

@Service
public class PostService {

    @PreAuthorize("@postGuard.canEdit(#postId, authentication)")
    public Post update(Long postId, PostForm form) { ... }
}

Ownership is a query, so let a small guard bean answer it from SpEL.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Spring Security course, and every lesson in it is listed on the Spring Security contents page.