Project 3: Secured Blog API (JWT + Roles + Comments)
This project builds a secure Blog backend. Only <code>ADMIN</code> users can create posts. Any <code>USER</code> can read posts and leave comments. This ties together Phase 7 (Spring Security 6, JWT, BCrypt, <code>@PreAuthorize</code>) and Phase 4 (JPA).
The Hyderabad Times Newspaper Analogy:
At a newspaper, there are two types of people:
- Journalists (ADMIN): They have the authority to write and publish articles.
- Readers (USER): They can read the articles and write comments in the feedback section.
If a Reader tries to access the "Publish Article" button, security stops them. We implement this using JWT and Spring Security's @PreAuthorize.
- Spring Security 6 (Stateless):
SecurityFilterChainconfigured withSessionCreationPolicy.STATELESSand our customJwtAuthenticationFilter. - @EnableMethodSecurity: Enables
@PreAuthorize("hasRole('ADMIN')")on the controller methods. - BCryptPasswordEncoder: Used during the
/registerendpoint to hash passwords before saving to the DB.
Assume UserEntity, PostEntity, and Repositories exist.
1. Security Configuration (SecurityConfig.java)
package com.devinhyderabad;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, JwtAuthenticationFilter jwtFilter) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/posts/**").permitAll() // Reading posts is public
.anyRequest().authenticated()
)
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}2. The Secured Controller (PostController.java)
package com.devinhyderabad;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/posts")
public class PostController {
// 1. Public: Anyone can read posts
@GetMapping
public String getAllPosts() {
return "List of all blog posts.";
}
// 2. Secured: Only users with ROLE_ADMIN can create posts
@PostMapping
@PreAuthorize("hasRole('ADMIN')")
public String createPost(@RequestBody String postContent) {
return "Post created successfully by Admin: " + postContent;
}
// 3. Secured: Any authenticated user (USER or ADMIN) can comment
@PostMapping("/{id}/comments")
@PreAuthorize("hasAnyRole('USER', 'ADMIN')")
public String addComment(@PathVariable Long id) {
return "Comment added to post " + id;
}
}The most critical part of this project is the JwtAuthenticationFilter. It must extract the JWT from the Authorization: Bearer <token> header, validate the signature, extract the username and roles, and set the UsernamePasswordAuthenticationToken inside the SecurityContextHolder. If the filter doesn't set the authorities correctly, @PreAuthorize will always return 403 Forbidden.
Interview Note: The @EnableMethodSecurity annotation is the Spring Security 6 replacement for the now-deprecated @EnableGlobalMethodSecurity. It automatically enables @PreAuthorize, @PostAuthorize, and @Secured. Interviewers often ask: "Why does hasRole('ADMIN') require the role to be stored as ROLE_ADMIN in the database?" Because Spring Security automatically prefixes it.
Key Takeaways
- ✅ @EnableMethodSecurity enables @PreAuthorize for method-level role-based access control
- ✅ JwtAuthenticationFilter validates the token and sets the SecurityContext for each request
- ✅ SessionCreationPolicy.STATELESS ensures no HTTP session — every request carries its own JWT
- ✅ hasRole('ADMIN') looks for ROLE_ADMIN in the database — Spring adds the ROLE_ prefix automatically
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login