java 47 lines · 7 steps

Method-level authorization with @PreAuthorize in Spring

A Spring REST controller guards each endpoint with a different SpEL security rule via @PreAuthorize.

Explained by highlit
1@RestController
2@RequestMapping("/api/documents")
3@RequiredArgsConstructor
4public class DocumentController {
5 
6 private final DocumentService documentService;
7 
8 @GetMapping
9 @PreAuthorize("hasAnyRole('USER', 'ADMIN')")
10 public List<DocumentDto> list(@AuthenticationPrincipal UserPrincipal principal) {
11 return documentService.findAccessibleFor(principal.getId());
12 }
13 
14 @GetMapping("/{id}")
15 @PreAuthorize("@documentSecurity.canRead(#id, authentication)")
16 public DocumentDto get(@PathVariable Long id) {
17 return documentService.findById(id);
18 }
19 
20 @PostMapping
21 @PreAuthorize("hasRole('EDITOR')")
22 public ResponseEntity<DocumentDto> create(@Valid @RequestBody CreateDocumentRequest request,
23 @AuthenticationPrincipal UserPrincipal principal) {
24 DocumentDto created = documentService.create(request, principal.getId());
25 return ResponseEntity.status(HttpStatus.CREATED).body(created);
26 }
27 
28 @PutMapping("/{id}")
29 @PreAuthorize("hasRole('EDITOR') and @documentSecurity.isOwner(#id, authentication)")
30 public DocumentDto update(@PathVariable Long id,
31 @Valid @RequestBody UpdateDocumentRequest request) {
32 return documentService.update(id, request);
33 }
34 
35 @DeleteMapping("/{id}")
36 @PreAuthorize("hasRole('ADMIN')")
37 @ResponseStatus(HttpStatus.NO_CONTENT)
38 public void delete(@PathVariable Long id) {
39 documentService.delete(id);
40 }
41 
42 @PostMapping("/{id}/publish")
43 @PreAuthorize("hasAnyRole('EDITOR', 'ADMIN') and #request.reviewed")
44 public DocumentDto publish(@PathVariable Long id, @RequestBody PublishRequest request) {
45 return documentService.publish(id);
46 }
47}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1@PreAuthorize accepts SpEL, so each endpoint can express its own authorization rule inline.
  2. 2Beans like @documentSecurity let you move per-resource ownership checks out of the controller body.
  3. 3Combining role checks with method arguments (#id, #request) enables fine-grained, context-aware access control.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Method-level authorization with @PreAuthorize in Spring — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code