Unit-IV: Backend Engineering with
Java Spring Boot
1. Spring Boot Ecosystem and IoC / Dependency Injection
2. Spring Data JPA: Repositories and Entity Mapping
3. RESTful Controllers: Handling HTTP Methods
4. API Versioning Strategies
5. Spring Security: Authentication and Authorization
6. Securing APIs with JSON Web Token (JWT)
7. Role-Based Access Control (RBAC) using JWT Claims
8. Input Validation and Global Exception Handling
9. API Documentation with Swagger / OpenAPI
Spring Boot Ecosystem and IoC / Dependency Injection
What is Spring Boot?
Spring Boot is an open-source Java-based framework built on top of the Spring Framework. It
simplifies the development of production-ready applications by providing auto-configuration,
embedded servers, and starter dependencies. It eliminates boilerplate configuration so
developers can focus on business logic.
Spring Boot Ecosystem Components
Component Purpose
Spring Core Foundation: IoC container and Dependency Injection
Spring MVC Build web applications and REST APIs
Spring Data JPA Simplify database operations using ORM
Spring Security Authentication, Authorization, and API security
Spring Boot Auto-configuration and embedded server wrapper
Spring Cloud Microservices, service discovery, config servers
Key Features of Spring Boot
• Auto-Configuration: Automatically configures beans based on classpath dependencies.
• Starter Dependencies: Pre-packaged dependency sets (e.g., spring-boot-starter-web).
• Embedded Server: Ships with Tomcat/Jetty; no external server deployment needed.
• Spring Initializr: Web tool to bootstrap a Spring Boot project quickly ([Link]).
• Actuator: Built-in production monitoring endpoints such as /health and /metrics.
Inversion of Control (IoC)
IoC is a design principle where the control of object creation and lifecycle is handed over from
the application code to the Spring IoC container. In a traditional approach, a class creates its
own dependencies. With IoC, the container creates and manages them on behalf of the class.
Traditional: UserService creates a DatabaseHelper object inside its own constructor.
IoC: Spring container creates DatabaseHelper and supplies it to UserService automatically.
Dependency Injection (DI)
Dependency Injection is the mechanism that implements IoC. Instead of a class instantiating its
dependencies, they are injected by the Spring container.
DI Type Description
Constructor Injection Dependencies passed via constructor – recommended best
practice
Setter Injection Dependencies supplied via setter methods after object
creation
Field Injection Dependencies injected directly on fields using @Autowired –
not recommended for testing
Important Spring Annotations
Annotation Meaning
@SpringBootApplication Entry point; combines @Configuration,
@EnableAutoConfiguration, @ComponentScan
@Component Marks a class as a Spring-managed bean
@Service Specialization of @Component for the service layer
@Repository Specialization of @Component for the DAO / data access
layer
@RestController Marks a class as a REST web controller; combines
@Controller and @ResponseBody
@Autowired Injects a matching bean automatically
@Bean Declares a method that returns a Spring-managed bean
@Configuration Marks a class as a source of bean definitions
Hands-on:
Step 1: Create a Spring Boot Project
Go to [Link] and configure: Project = Maven, Language = Java, Dependencies =
Spring Web, Spring Data JPA, H2 Database. Download, extract, and open in IntelliJ IDEA or VS
Code.
Step 2: Main Application Class
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
[Link]([Link], args);
}
}
This single annotation bootstraps the entire Spring context automatically.
Step 3: Constructor Injection Example
@Service
public class UserService {
private final UserRepository userRepository;
// Constructor Injection (recommended)
public UserService(UserRepository userRepository) {
[Link] = userRepository;
}
}
Step 4: React Integration Concept
Spring Boot runs as the backend API server on port 8080. React runs as a separate frontend on
port 3000. React calls the Spring Boot API using fetch() or Axios. Enable CORS on the Spring
Boot side to allow cross-origin requests from React.
@CrossOrigin(origins = "[Link]
@RestController
public class UserController { ... }
// React fetching Spring Boot API
fetch('[Link]
.then(res => [Link]())
.then(data => setUsers(data));
Spring Data JPA – Repositories and Entity Mapping
What is JPA?
Java Persistence API (JPA) is a specification for managing relational data in Java. It uses
Object-Relational Mapping (ORM) to map Java objects called entities to database tables. This
eliminates the need for writing raw SQL in most cases.
What is Spring Data JPA?
Spring Data JPA is a layer built on top of JPA (using Hibernate as the default implementation). It
reduces boilerplate code by providing ready-made repository interfaces with built-in CRUD
operations. Developers only define an interface; Spring generates the implementation at
runtime.
Entity Mapping Annotations
Annotation Purpose
@Entity Marks the class as a JPA entity mapped to a database table
@Table(name=...) Specifies the database table name (optional if class name
matches)
@Id Marks the primary key field
@GeneratedValue Automatically generates the primary key value
@Column(name=...) Maps a field to a specific column name with constraints
@OneToMany Defines a one-to-many relationship between two entities
@ManyToOne Defines a many-to-one relationship
@JoinColumn Specifies the foreign key column used in a relationship
Repository Interface Hierarchy
Interface Provides
Repository<T, ID> Marker interface with no methods
CrudRepository<T, ID> save(), findById(), findAll(), delete()
PagingAndSortingRepository<T, Pagination and sorting support
ID>
JpaRepository<T, ID> All the above plus flush() and batch operations
JpaRepository is the most commonly used. Extend it in your repository interface.
Derived Query Methods
Spring Data JPA generates SQL from method names automatically. No implementation code is
needed.
findByEmail(String email) --> SELECT * FROM users WHERE email = ?
findByAgeGreaterThan(int age) --> SELECT * FROM users WHERE age > ?
findByNameAndCity(String n, String c) --> AND condition
Hands-on:
Step 1: Add Dependency ([Link])
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
Step 2: Define an Entity
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = [Link])
private Long id;
@Column(nullable = false)
private String name;
@Column(unique = true, nullable = false)
private String email;
// Getters and Setters
}
Step 3: Create Repository Interface
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
List<User> findByName(String name);
}
Spring auto-generates the implementation. No SQL queries needed.
Step 4: [Link]
[Link]=jdbc:h2:mem:testdb
[Link]-class-name=[Link]
[Link]-auto=update
[Link]-sql=true
[Link]=true
Step 5: Service Layer
@Service
public class UserService {
private final UserRepository repo;
public UserService(UserRepository repo) { [Link] = repo; }
public List<User> getAllUsers() { return [Link](); }
public User saveUser(User user) { return [Link](user); }
public Optional<User> findByEmail(String email) {
return [Link](email);
}
}
React Integration
// Fetch users from Spring Boot API
useEffect(() => {
fetch('[Link]
.then(r => [Link]())
.then(data => setUsers(data));
}, []);
RESTful Controllers – Handling HTTP Methods
What is REST?
REST (Representational State Transfer) is an architectural style for designing networked APIs.
A RESTful API uses standard HTTP methods to perform operations on resources identified by
URLs. It is stateless, meaning each request contains all the information needed to process it.
HTTP Methods and Their Meaning
HTTP Method Operation Example URL
GET Read or retrieve data /api/users or /api/users/{id}
POST Create a new resource /api/users
PUT Replace existing resource (full /api/users/{id}
update)
PATCH Partially update a resource /api/users/{id}
DELETE Remove a resource /api/users/{id}
Key REST Controller Annotations
Annotation Usage
@RestController Combines @Controller and @ResponseBody; returns JSON
by default
@RequestMapping Maps a base URL to a class or method
@GetMapping Handles HTTP GET requests
@PostMapping Handles HTTP POST requests
@PutMapping Handles HTTP PUT requests
@DeleteMapping Handles HTTP DELETE requests
@PathVariable Extracts a value from the URL path e.g., /users/{id}
@RequestBody Reads and deserializes JSON from the request body
@RequestParam Reads query parameters from the URL e.g., ?name=Alice
ResponseEntity<T> Allows control over HTTP status code and response body
HTTP Response Status Codes
Code Meaning
200 OK Request was successful
201 Created Resource created successfully
204 No Content Successful, no body to return
400 Bad Request Invalid input from client
401 Unauthorized Authentication required
403 Forbidden Authenticated but not authorized
404 Not Found Resource does not exist
500 Internal Server Error Unexpected server-side error
Hands-on: Full CRUD REST Controller
@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "[Link]
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
[Link] = userService;
}
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
return [Link]([Link]());
}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
return [Link](id)
.map(ResponseEntity::ok)
.orElse([Link]().build());
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
return [Link](201).body([Link](user));
}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id,
@RequestBody User user) {
[Link](id);
return [Link]([Link](user));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
[Link](id);
return [Link]().build();
}
}
React: POST Request to Create a User
const createUser = async (userData) => {
const response = await fetch('[Link] {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: [Link](userData)
});
return [Link]();
};
API Versioning Strategies
Why API Versioning?
As an API evolves, breaking changes may be introduced – for example, renaming fields or
changing response structure. Versioning allows old clients (such as a React frontend using v1)
to continue working without disruption while new clients adopt v2. Without versioning, any API
update risks breaking existing consumers.
Types of API Versioning
Strategy Description and Example
URI Versioning Version embedded in URL path: /api/v1/users, /api/v2/users
Request Parameter Version as a query parameter: /api/users?version=1
Header Versioning Custom request header: X-API-Version: 1
Content Negotiation Version in Accept header: Accept: application/[Link].v1+json
Comparison of Versioning Strategies
Strategy Advantage Disadvantage
URI Versioning Easy to test in browser; clear URL pollution; not purely
and visible RESTful
Request Param Simple to implement Easy to miss; less visible to
developers
Header Versioning Clean URLs; best REST practice Cannot test directly in a browser
URL
Content Negotiation RESTfully correct approach Complex to implement and
consume
Hands-on
URI Versioning in Spring Boot
@RestController
@RequestMapping("/api/v1/users")
public class UserControllerV1 {
@GetMapping
public List<String> getUsers() {
return [Link]("Alice", "Bob");
}
}
@RestController
@RequestMapping("/api/v2/users")
public class UserControllerV2 {
@GetMapping
public List<UserDTO> getUsers() {
return [Link]();
}
}
Header Versioning Example
@GetMapping(value = "/users", headers = "X-API-Version=1")
public ResponseEntity<?> getUsersV1() { ... }
@GetMapping(value = "/users", headers = "X-API-Version=2")
public ResponseEntity<?> getUsersV2() { ... }
React: Calling a Versioned API
const API_VERSION = 'v1';
fetch(`[Link]
.then(r => [Link]())
.then(data => setUsers(data));
Spring Security – Authentication and Authorization
What is Spring Security?
Spring Security is a powerful and highly customizable framework that provides authentication
and authorization to Java applications. It protects APIs from unauthorized access and supports
various security mechanisms including HTTP Basic, Form Login, OAuth2, and JWT.
Authentication vs Authorization
Concept Definition
Authentication Verifying WHO the user is – e.g., checking username and
password
Authorization Verifying WHAT the user is allowed to do – e.g., checking
roles
Authentication always happens before Authorization.
Spring Security Core Components
Component Role
SecurityFilterChain A chain of filters that every HTTP request passes through
AuthenticationManager Coordinates the authentication process
UserDetailsService Loads user-specific data from the database or any data source
UserDetails Holds user information: username, password, roles
PasswordEncoder Encodes and verifies passwords; BCryptPasswordEncoder is
recommended
GrantedAuthority Represents a role or permission granted to a user
Security Filter Chain
Every HTTP request passes through a chain of filters before reaching the controller. Common
filters include UsernamePasswordAuthenticationFilter for form login, BasicAuthenticationFilter
for HTTP Basic auth, and custom JwtAuthenticationFilter for JWT-based authentication.
Hands-on
Step 1: Add Dependency
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Step 2: Custom UserDetailsService
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) {
User user = [Link](email)
.orElseThrow(() ->
new UsernameNotFoundException("User not found"));
return [Link]
.withUsername([Link]())
.password([Link]())
.roles([Link]())
.build();
}
}
Step 3: Security Configuration
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
[Link]().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
);
return [Link]();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Securing APIs with JSON Web Token (JWT)
What is JWT?
JSON Web Token (JWT) is a compact, self-contained token used to securely transmit
information between parties as a JSON object. It is digitally signed so the receiver can verify its
authenticity. JWTs are used in stateless authentication – the server does not need to store
session data because all information is inside the token.
JWT Structure
A JWT has three parts separated by dots: [Link]
Part Contains
Header Algorithm (e.g., HS256) and token type (JWT)
Payload Claims: user data such as sub (subject), roles, exp (expiry
time)
Signature HMAC of header + payload using a secret key; ensures the
token has not been tampered with
JWT Authentication Flow
• Step 1: User sends credentials (email and password) to /api/auth/login.
• Step 2: Server verifies credentials against the database.
• Step 3: Server generates a JWT token signed with a secret key and returns it.
• Step 4: React stores the token in localStorage.
• Step 5: Every subsequent API request includes the token in the Authorization header.
• Step 6: Server validates the token on each request. Valid token proceeds; invalid returns
401.
Advantages of JWT
• Stateless: No server-side session storage needed – scales easily.
• Self-contained: All user information is embedded inside the token.
• Cross-domain: Works seamlessly between React (port 3000) and Spring Boot (port
8080).
• Secure: Signature verification prevents tampering.
Hands-on
Step 1: Add JWT Dependency
<dependency>
<groupId>[Link]</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>[Link]</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
Step 2: JWT Utility Class
@Component
public class JwtUtil {
private final String SECRET = "mySecretKey12345678901234567890";
public String generateToken(String username) {
return [Link]()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(
new Date([Link]() + 86400000)) // 1 day
.signWith([Link]([Link]()))
.compact();
}
public String extractUsername(String token) {
return [Link]()
.setSigningKey([Link]([Link]()))
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
}
}
Step 3: Authentication Controller
@RestController
@RequestMapping("/api/auth")
public class AuthController {
private final AuthenticationManager authManager;
private final JwtUtil jwtUtil;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
[Link](new UsernamePasswordAuthenticationToken(
[Link](), [Link]()));
String token = [Link]([Link]());
return [Link]([Link]("token", token));
}
}
React: Login and Store JWT
const login = async (email, password) => {
const res = await fetch('[Link] {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: [Link]({ email, password })
});
const data = await [Link]();
[Link]('token', [Link]);
};
// Attach token to protected requests
const getUsers = async () => {
const token = [Link]('token');
const res = await fetch('[Link] {
headers: { 'Authorization': `Bearer ${token}` }
});
return [Link]();
};
Role-Based Access Control (RBAC) using JWT Claims
What is RBAC?
Role-Based Access Control (RBAC) restricts access to system resources based on the roles
assigned to users. Instead of assigning permissions to individual users, roles such as ADMIN,
USER, and MANAGER are created with specific permissions, and users are assigned those
roles.
Embedding Roles in JWT Claims
When generating a JWT token, the user's role is embedded as a claim inside the payload. The
Spring Security filter extracts this role from the token and stores it in the SecurityContext so
authorization decisions can be made.
// JWT Payload (decoded) Example
{
"sub": "alice@[Link]",
"role": "ADMIN",
"exp": 1700000000
}
Access Control Annotations
Annotation / Expression Usage
@PreAuthorize("hasRole('ADMIN')") Only ADMIN users can access this method
@PreAuthorize("hasAnyRole('ADMIN','USER')") Either ADMIN or USER role can access
@Secured("ROLE_ADMIN") Method-level security using older
@Secured annotation
.hasRole("ADMIN") URL-pattern-based access control in
SecurityFilterChain
Hands-on
Step 1: Include Role in JWT Token
public String generateToken(String username, String role) {
return [Link]()
.setSubject(username)
.claim("role", role)
.setExpiration(new Date([Link]() + 86400000))
.signWith([Link]([Link]()))
.compact();
}
Step 2: Extract Role in JWT Filter and Set in SecurityContext
Claims claims = [Link]()
.setSigningKey(key).build()
.parseClaimsJws(token).getBody();
String role = [Link]("role", [Link]);
List<GrantedAuthority> authorities = [Link](
new SimpleGrantedAuthority("ROLE_" + role));
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(
username, null, authorities);
[Link]().setAuthentication(auth);
Step 3: Protect Endpoints by Role in Security Config
[Link]().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/users/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
);
Step 4: Method-Level Security with @PreAuthorize
@EnableMethodSecurity // Add to the main config class
@GetMapping("/admin/dashboard")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> adminDashboard() {
return [Link]("Welcome Admin");
}
React: Role-Based Conditional Rendering
import { jwtDecode } from 'jwt-decode';
const token = [Link]('token');
const decoded = jwtDecode(token);
const role = [Link];
{role === 'ADMIN' && <AdminPanel />}
{role === 'USER' && <UserDashboard />}
Input Validation and Global Exception Handling
Why Input Validation?
Validating input at the server side ensures data integrity, prevents invalid data from entering the
database, and protects against malicious input. Spring Boot integrates the Bean Validation API
(Jakarta Validation) through the spring-boot-starter-validation dependency.
Common Validation Annotations
Annotation Validates That...
@NotNull Field value is not null
@NotBlank String is not null, not empty, and not whitespace only
@NotEmpty String or collection is not null and not empty
@Size(min, max) String or collection size is within the given range
@Min(value) Number is greater than or equal to the given value
@Max(value) Number is less than or equal to the given value
@Email String is a valid email address format
@Pattern(regexp) String matches the specified regular expression
@Positive Number is strictly greater than zero
Global Exception Handling
Instead of handling exceptions in every controller method, Spring Boot provides
@RestControllerAdvice to centralize exception handling across all controllers. This produces
uniform, structured error responses for every API consumer.
Common Exceptions
Exception Scenario
MethodArgumentNotValidException Triggered when @Valid fails on a @RequestBody
ConstraintViolationException Triggered when @Validated fails on @PathVariable or
@RequestParam
ResourceNotFoundException Thrown when a requested record is not found in the
(custom) database
Exception (global fallback) Catches any unhandled exception as a last resort
Hands-on
Step 1: Add Dependency
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Step 2: Add Validation Constraints to DTO
public class UserDTO {
@NotBlank(message = "Name is required")
@Size(min = 2, max = 50, message = "Name must be 2-50 characters")
private String name;
@NotBlank(message = "Email is required")
@Email(message = "Invalid email format")
private String email;
@NotNull(message = "Age is required")
@Min(value = 18, message = "Age must be at least 18")
private Integer age;
}
Step 3: Enable Validation in Controller
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody UserDTO dto) {
// @Valid triggers validation automatically
// If any constraint fails, MethodArgumentNotValidException is thrown
User saved = [Link](dto);
return [Link](201).body(saved);
}
Step 4: Custom Exception Class
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
Step 5: Global Exception Handler
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler([Link])
public ResponseEntity<Map<String, String>> handleValidation(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
[Link]().getFieldErrors().forEach(err ->
[Link]([Link](), [Link]()));
return [Link]().body(errors);
}
@ExceptionHandler([Link])
public ResponseEntity<String> handleNotFound(
ResourceNotFoundException ex) {
return [Link](404).body([Link]());
}
@ExceptionHandler([Link])
public ResponseEntity<String> handleGeneral(Exception ex) {
return [Link](500).body("Internal Server Error");
}
}
React: Displaying Validation Errors
const createUser = async (data) => {
const res = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: [Link](data)
});
if (![Link]) {
const errors = await [Link]();
setErrors(errors); // e.g. { name: 'Name is required', email: '...' }
}
};
API Documentation with Swagger / OpenAPI
What is Swagger / OpenAPI?
OpenAPI (formerly Swagger) is a specification for describing RESTful APIs in a machine-
readable JSON or YAML format. Swagger UI reads this specification and generates interactive
API documentation automatically. It allows developers and testers to explore and invoke API
endpoints directly from a browser without writing any client code.
Why Use Swagger?
• Automatically generates documentation from Spring Boot code and annotations.
• Provides an interactive browser-based UI to test endpoints without Postman.
• Helps React developers understand what endpoints exist and what they return.
• Acts as a living contract between backend and frontend development teams.
springdoc-openapi Library
The springdoc-openapi library integrates OpenAPI 3 with Spring Boot. Once added as a
dependency, it auto-generates the API spec at /v3/api-docs and serves Swagger UI at
/[Link] with zero additional configuration.
Key Swagger Annotations
Annotation Purpose
@Tag(name, description) Groups related endpoints under a named label in Swagger
UI
@Operation(summary, Documents a single API endpoint method
description)
@ApiResponse(responseCode, Documents a specific HTTP response for an endpoint
description)
@Parameter(description) Documents an individual method parameter
@Schema(description) Documents a model field in a DTO or Entity class
@SecurityRequirement(name) Marks an endpoint as requiring authentication in Swagger
UI
Hands-on
Step 1: Add Dependency
<dependency>
<groupId>[Link]</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.1.0</version>
</dependency>
Step 2: Access Swagger UI
After adding the dependency and starting the application, open:
[Link]
The OpenAPI JSON specification is available at:
[Link]
Step 3: Configure OpenAPI Metadata
@Configuration
public class OpenAPIConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("User Management API")
.version("1.0")
.description("Spring Boot REST API with JWT Security"));
}
}
Step 4: Annotate Controller for Documentation
@Tag(name = "User API", description = "Operations for managing users")
@RestController
@RequestMapping("/api/users")
public class UserController {
@Operation(summary = "Get all users",
description = "Returns a list of all registered users")
@ApiResponse(responseCode = "200", description = "Users retrieved")
@GetMapping
public ResponseEntity<List<User>> getAllUsers() { ... }
@Operation(summary = "Create a new user")
@ApiResponse(responseCode = "201", description = "User created")
@ApiResponse(responseCode = "400", description = "Validation error")
@PostMapping
public ResponseEntity<User> createUser(@RequestBody UserDTO dto) { ... }
}
Step 5: Add JWT Authorization to Swagger UI
@Bean
public OpenAPI openAPIWithJWT() {
return new OpenAPI()
.addSecurityItem(new SecurityRequirement().addList("Bearer"))
.components(new Components().addSecuritySchemes("Bearer",
new SecurityScheme()
.type([Link])
.scheme("bearer")
.bearerFormat("JWT")));
}
After this configuration, Swagger UI will display an Authorize button. Paste your JWT token
there to test secured endpoints directly from the browser without Postman.
Step 6: Permit Swagger URLs in Security Config
.requestMatchers("/swagger-ui/**",
"/v3/api-docs/**",
"/[Link]").permitAll()