Method Security, CORS & CSRF
@PreAuthorize for fine-grained rules, plus what CORS and CSRF are and how Spring handles them.
On this page
URL-based rules cover most access control, but sometimes a rule belongs on the method, close to the logic it protects. And two acronyms - CORS and CSRF - trip up nearly every web developer. This lesson finishes the Security module by tidying up all three.
Method security with @PreAuthorize
Enable it once, then annotate methods with an authorization expression that's checked before the method runs:
@Configuration
@EnableMethodSecurity // turns on @PreAuthorize/@PostAuthorize
class MethodSecurityConfig {}@Service
class BookService {
@PreAuthorize("hasRole('LIBRARIAN')")
public void delete(String isbn) { ... } // 403 unless the caller is a LIBRARIAN
@PreAuthorize("#memberId == authentication.principal.id or hasRole('LIBRARIAN')")
public List<Loan> loansOf(Long memberId) { ... } // your own loans, or a librarian's view
}The second example shows method security's real strength: expressions can reference method arguments and the current user, so you can enforce "a member may see only their own data" - a rule that URL matchers can't express.
URL rules for coarse, method rules for fine
Use authorizeHttpRequests for broad, endpoint-level rules (this path needs LIBRARIAN),
and @PreAuthorize for fine-grained, data-aware rules (you may act only on your own
records). They complement each other; you don't have to choose one.
CORS: who may call your API from a browser
CORS (Cross-Origin Resource Sharing) is a browser security feature. By default a
web page at app.bookvault.dev cannot call an API at api.bookvault.dev (a different
origin) unless the API explicitly allows it. You opt in per allowed origin:
http.cors(cors -> cors.configurationSource(request -> {
var config = new CorsConfiguration();
config.setAllowedOrigins(List.of("https://app.bookvault.dev"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
return config;
}));CORS isn't a lock on your API - it's the browser asking your API "may this website talk to you?" and honoring the answer. Non-browser clients ignore it entirely.
CSRF: and why you disabled it
CSRF (Cross-Site Request Forgery) tricks a logged-in user's browser into sending an unwanted request using their session cookie. Spring Security's CSRF protection defends session-cookie apps. But BookVault authenticates with a Bearer token, not a cookie the browser attaches automatically - so there's nothing for CSRF to exploit, which is exactly why you disabled CSRF for the stateless API earlier.
Keep CSRF on for cookie/session apps
Disabling CSRF is correct only for stateless, token-authenticated APIs. If you build a traditional server-rendered app that authenticates with a session cookie, leave CSRF protection enabled - it's a genuine defense there.
BookVault must let a member view their own active loans but not anyone else's, while
a LIBRARIAN may view any member's. A URL rule like hasRole('MEMBER') can't express
"only your own." Which mechanism do you use, and roughly what expression enforces it?
What can @PreAuthorize express that URL-based (authorizeHttpRequests) rules cannot?
Key takeaways
- @EnableMethodSecurity + @PreAuthorize put authorization on methods, checked before they run.
- Method expressions can reference arguments and the current user, enabling fine-grained rules like 'only your own data' that URL matchers can't express.
- Use URL rules for coarse endpoint access and method security for fine-grained, data-aware checks - they complement each other.
- CORS is a browser feature: your API opts in to which cross-origin sites may call it; non-browser clients ignore it.
- CSRF protection defends cookie/session apps; it's safe to disable only for stateless, Bearer-token APIs like BookVault.