0% found this document useful (0 votes)
23 views17 pages

Spring Boot Security Basics with JWT

The document outlines the essential classes and steps for configuring security in a Spring Boot application using JWT. It covers prerequisites, package structure, user model setup, JWT service implementation, and security configuration with detailed code snippets. The guide emphasizes a step-by-step approach to building a secure application while maintaining clarity and separation of concerns.

Uploaded by

harshitha billa
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
23 views17 pages

Spring Boot Security Basics with JWT

The document outlines the essential classes and steps for configuring security in a Spring Boot application using JWT. It covers prerequisites, package structure, user model setup, JWT service implementation, and security configuration with detailed code snippets. The guide emphasizes a step-by-step approach to building a secure application while maintaining clarity and separation of concerns.

Uploaded by

harshitha billa
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Security Configuration in Spring Boot (The basic) 🚀🔒

Although security configuration might seem daunting, it actually boils down to a few key classes
that form the core foundation. With these foundational classes and a well-defined structure, you
can tackle any level of complexity in security implementation. Below, we'll introduce these
essential classes briefly before diving deeper into each one later:

● ApplicationConfig: Configures application-specific beans.


● JwtAuthenticationFilter: Filters incoming requests to check for valid JWT tokens.
● JwtService: Handles JWT token creation and validation.
● WebSecurityConfig: Configures web security, including authentication and
authorization.
● UserDetailsImpl: Implements the UserDetails interface to load user-specific data.

These classes serve as the building blocks of Java security in Spring Boot. Let's explore each
one in more detail to understand how they work together to secure your application.

🌟 Let's embark on building our Spring Security application step-by-step:


● First: We'll establish our package structure for organization.
● Second: Successfully set up our user model with a registration endpoint.
● Third: Implement the [Link] interface and its implementation class.
● Fourth: Dive into configuring the basic security features in the config package. 🚀

Hold ON! WAIT A MINUTE.

Before we dive into the Spring Security development using JSON Web Tokens (JWT), there are
several prerequisites that must be met to get the application up and running. This guide won't go
deep into individual setups since that is not the focus of this article. Here’s a checklist to ensure
you have everything in place:

1. Spring Dependencies

Add the following dependencies to your [Link]. If you're initializing your project via Spring
Initializr, you can include these dependencies there:

● Spring Security
● Spring Web
● Spring DevTools (optional)
● MySQL Connector/Driver
● Spring Data JPA
● Lombok (optional)
● Spring Validation (optional)

2. JWT Dependencies

Include the JWT dependencies from the Maven repository:

● jjwt-api
● jjwt-impl
● jjwt-jackson

3. Application Properties

Set up your [Link] for database connectivity and JWT configuration.

Database Setup
# ===========================================================
# - DATABASE-PROPERTIES
# ===========================================================
[Link]=jdbc:mysql://localhost:3306/your_database_name
[Link]=your_database_username
[Link]=your_database_password
[Link]=[Link]
[Link]-auto=update

JWT Secret Key

# ===========================================================
# - JWT-PROPERTIES
# ===========================================================
[Link]=your_generated_secret_key

Generating the Secret Key

To generate your secret key, visit this website:

1. From the Algorithm dropdown, select HS256.


2. From the next dropdown that appears (Bytes), select 64.
3. Click on Generate to generate the key.
4. Copy the generated key and replace your_generated_secret_key in the properties file.
Note: Ensure there are no breaks in the key (e.g., white spaces, line breaks). It should look
something like this:
TIGzpevixRVpMpbTWT2l6CB+tMMDBDB2lZ69ZYIp4vry2AKZj3EQhgQJ5C3KOIyfjUbyNkHPd0
+DIc.

😎
🚀
With these prerequisites in place, you are ready to launch into the Spring Security development
using JSON Web Tokens. Let's get started!

NOW WE CAN COMMENCE

Step 1: We'll establish our package structure for organization.

Explaining the Structure to Be Used 🏗️


Organizing your Spring Boot application begins with selecting the optimal package structure,
vital for scalability and maintaining clarity. Among several conventions like the Traditional
Layered Structure, Feature-Based Structure, and Self-Contained Structure, we will focus solely
on the Traditional Layered Structure for this discussion.

Traditional Layered Structure:

This approach categorizes components by their respective roles:

● Controller Layer: Handles HTTP requests and responses.


● Service Layer: Implements business logic.
● Repository Layer: Manages data persistence.
● Model Layer: Defines the application's data structure.
● Config: Manages application-wide configuration.
● Dto: Facilitates data transfer between layers.

This widely adopted structure ensures clear separation of concerns and supports robust
application development practices.
The image above represents the package structure we will be using in this project. So, go
ahead and include the following packages in your Java application:

Step 2: Successfully set up our user model with a registration endpoint.

Basic Setup for User Model With Only Registration Endpoint 👥📦


Let's dive into the essential setup: building your user model, repository, DTOs, services, and
controllers. We'll guide you step-by-step, keeping it engaging and straightforward.
Model Package: model - [Link]

@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
public class User {
@Id
@GeneratedValue(strategy = [Link])
private Long id;

private String email;


private String password;
}

Data Transfer Object (DTO) 📤


Request DTOs: These classes handle user requests.

● Package: [Link]

@Data
@Builder
public class UserRequestDto {
@NotBlank(message = "Username is mandatory")
private String email;

@NotBlank(message = "Password is mandatory")


private String password;
}

Login Response DTO: This class handles the login response displayed on Postman.

● Package: [Link]

@Data
@AllArgsConstructor
@Builder
public class LoginResponseDto {
private String username;
private String token;
}

Registration Response DTO: This class handles the response after user registration,
displayed on Postman.

● Package: [Link]

@Data
@Builder
public class RegistrationResponseDto {
private String message;
}

Repository Package 💾
User Repository: Manages database operations.

● Package: [Link]

public interface UserRepository extends JpaRepository<User, Long> {


Optional<User> findByEmail(String email);
}

Service Package 🚀
User Service Interface: Declares methods to be implemented.

● Package: [Link]

public interface UserService {


RegistrationResponseDto userReg(UserRequestDto userRequestDto);

// Todo: this would be update later


}
Service Package 🚀
User Service Implementation: Implements the service logic.

● Package: [Link]

@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final AuthenticationManager authenticationManager;
private final JwtService jwtService;

@Override
public RegistrationResponseDto userReg(UserRequestDto userRequestDto) {
User userExist =
[Link]([Link]()).orElse(null);

if(userExist != null){
return [Link]()
.message("User Already Exist")
.build();
}

User user = [Link]()


.email([Link]())

.password([Link]([Link]()))
.build();

[Link](user);

return [Link]()
.message("Registration Successful")
.build();
}
// will include the user Login method later after ther security
// after the security setup is completed
}

NB: 🌟 To ensure complete separation of concerns, we're doing things a bit differently with
UserDetailsImpl. Normally, you'd see class User extend UserDetails for simplicity,

🚫
but we're all about keeping things clean and readable. We want our entity class to stay focused
without getting tangled up in the program's lifecycle (bad practice alert! ). In our service
package, there's a [Link] class, and we'll have it implement
UserDetails, an in-built interface in Spring Security. Let's keep it tidy and secure!

Service Package 🚀
UserDetails Implementation: Implements the service logic for a default package in spring
security.

● Package: [Link]

@Data
@Builder
@RequiredArgsConstructor
public class UserDetailsImpl implements UserDetails {

private final User user;

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return [Link]();
}

@Override
public String getPassword() {
return [Link]();
}

@Override
public String getUsername() {
return [Link]();
}
}

Controller Package 📦
User Controller: Handles user endpoints and delegates requests to services.

● Package: [Link]

@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;

@PostMapping("/register")
public ResponseEntity<RegistrationResponseDto> register(@RequestBody
UserRequestDto userRequestDto) {
RegistrationResponseDto response =
[Link](userRequestDto);
return [Link](response);
}

// Will include user login later after completing the security


}

User Model Setup Completed! 🎉


With this setup, we've built a basic user registration endpoint. The security setup and user login
functionalities are still pending, but you can test the registration endpoint right away. By default,

🚀
Spring provides a password in the console, and the default username is "user". Give it a try and
see your setup in action!

Step 3: Implement the [Link] interface and its


implementation class.
Next: The Security Setup! 🚀🔐
We're diving into the security configuration, which, despite seeming complex, actually involves
just a few key classes. All the heavy lifting will be done in the config package.

We'll configure the following classes:

● [Link]
● [Link]
● [Link]

But first, let's set up [Link]. Let's get to it!

[Link] (Interface)
public interface JwtService {
// Extract username
String extractUsername(String token);

// Extract a single claim


<T> T extractClaim(String token, Function<Claims, T> claimResolver);

// Generate the token


String generateToken(UserDetails userDetails);

// Validate the token


boolean isTokenValid(String token, UserDetails userDetails);
}

[Link] (Implementation)
@Service
public class JwtServiceImpl implements JwtService {
@Value("${[Link]}")
private String jwtSecretKey;

@Override
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}

@Override
public <T> T extractClaim(String token, Function<Claims, T>
claimResolver) {
final Claims claims = extractAllClaims(token);
return [Link](claims);
}

@Override
public String generateToken(UserDetails userDetails) {
return generateToken(new HashMap<>(), userDetails);
}

@Override
public boolean isTokenValid(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return ([Link]([Link]()) &&
!isTokenExpired(token));
}

// Private methods

private Claims extractAllClaims(String token) {


return [Link]()
.setSigningKey(getSignInKey())
.build()
.parseClaimsJws(token)
.getBody();
}

private String generateToken(Map<String, Object> extractClaims,


UserDetails userDetails) {
return [Link]()
.setClaims(extractClaims)
.setSubject([Link]())
.setIssuedAt(new Date([Link]()))
.setExpiration(new Date([Link]() + 1000 *
60 * 20))
.signWith(getSignInKey(), SignatureAlgorithm.HS256)
.compact();
}

private boolean isTokenExpired(String token) {


return extractExpirationDate(token).before(new Date());
}
private Date extractExpirationDate(String token) {
return extractClaim(token, Claims::getExpiration);
}

private Key getSignInKey() {


byte[] keyBytes = [Link](jwtSecretKey);
return [Link](keyBytes);
}
}

With this setup done, we can now move on to configuring the big security guys! 💪

🚀
Step 4: Dive into configuring the basic security features in the
config package.

We've come a long way together, and I'm so proud of the progress you've made! 🚀
You're well

👍
on your way to becoming a senior dev. Don't quit now—just a few more steps and you'll be
done. Keep pushing, senior dev!

ONE Final Step! 🎯


Let's finish strong by configuring your application. Head over to your config package and set it
up like this:

[Link]

@Configuration
@RequiredArgsConstructor
public class ApplicationConfig {

private final UserRepository repository;

@Bean
UserDetailsService userDetails(){
return username -> {
Optional<User> user = [Link](username);
if ([Link]()) {
throw new UsernameNotFoundException("Username doesn't
exist");
}
return new UserDetailsImpl([Link]());
};
}

@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new
DaoAuthenticationProvider();
[Link](userDetails());
[Link](passwordEncoder());
return authProvider;
}

@Bean
public AuthenticationManager
authenticationManager(AuthenticationConfiguration config) throws Exception
{
return [Link]();
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

[Link]

@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

private final JwtService jwtService;


private final UserDetailsService userDetailsService;

@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain) throws
ServletException, IOException {
final String authHeader = [Link]("Authorization");
final String jwt;
final String userEmail;

if (authHeader == null || ![Link]("Bearer ")) {


[Link](request, response);
return;
}

jwt = [Link](7);
userEmail = [Link](jwt);

if (userEmail != null &&


[Link]().getAuthentication() == null) {
UserDetails userDetails =
[Link](userEmail);

if ([Link](jwt, userDetails)) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(
userDetails, null, [Link]()
);

[Link]().setAuthentication(authenticationToken);
}
}

[Link](request, response);
}
}

Now, for the final configuration: [Link]

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final AuthenticationProvider authenticationProvider;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws
Exception {
[Link](jwtAuthenticationFilter,
[Link])
.csrf(csrf -> [Link]())
.authorizeHttpRequests(req ->
[Link]("/api/auth/**").permitAll()
.anyRequest().authenticated())
.sessionManagement(session ->
[Link]([Link]))
.authenticationProvider(authenticationProvider);

return [Link]();
}
}

🎉 And that's a wrap! You can almost run your application now. But wait just a minute!
🛑 There are two tiny things left to do. We've only included user registration endpoint so
far. Now, we need to include the login endpoint for the application.

To do this, we'll update two files:

1. [Link] ([Link])
2. [Link]

Let's get started! 🚀

Step 1: Update [Link]

Find the commented area in [Link] and replace it with this snippet:

LoginResponseDto userLogin(UserRequestDto userRequestDto);


Step 2: Implement userLogin Method in [Link]

Now, let's implement the userLogin method. Update [Link] by


replacing the commented section with the following code:

@Override
public LoginResponseDto userLogin(UserRequestDto userRequestDto) {
[Link](new
UsernamePasswordAuthenticationToken(
[Link](), [Link]()
));

User user =
[Link]([Link]()).orElseThrow();
UserDetails userDetails =
[Link]().user(user).build();

var jwtToken = [Link](userDetails);

return [Link]()
.username([Link]())
.token(jwtToken)
.build();
}

Step 3: Update [Link]

Finally, update the [Link] class by replacing the commented section


with this snippet:

// login
@PostMapping("/login")
public ResponseEntity<LoginResponseDto> login(@RequestBody
UserRequestDto userRequestDto) {
LoginResponseDto response = [Link](userRequestDto);
return [Link](response);
}

✅ Now you’re all set! Go ahead and run your application. 🎉

You might also like