• Home
  • JWT Authentication and Authorization in SpringBoot 3.1 and Beyond

In this post, I will be explaining how we achieve JWT Security in the SpringBoot 3.1 version (with the newly deprecated changes).

Dependencies

We will use the following libraries for security

  1. SpringBoot starter security
  2. io.jsonwebtoken (jjwt-api/jjwt-impl/jjwt-jackson)

In Memory Authentication

This is the basic authentication we can implement to test if Spring security works well. (Not to be used in production preferably)

Implementation :

  • On adding dependency of Spring Security, by Default, a username and password (Found in logs), will be auto-implemented on the application
  • Also, a Login Logout functionality is auto-generated
  • To override this authentication, we create MyConfig class with @Configuration.
  • Create 3 beans: userDetailsService, passwordencoder and authenticationManager
  • userDetailsService: Here use a User builder to generate users [mention username, password, and roles]
  • once generate a return with new InMemoryUserDetailsManage
  • Create a password encoder bean returning BCryptPasswordEncoder.
  • authenticationManager has injected configBuilder and returned AuthManager.

@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.builder().
username(“Swapnil”)
.password(passwordEncoder().encode(“Password@123”)).roles(“ADMIN”).
build();
return new InMemoryUserDetailsManager(userDetails);
}

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

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration builder) throws Exception {
return builder.getAuthenticationManager();
}

JWT Authorization

Create security package and add following 3 classes :

JWTAuthenticationEntryPoint :

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter writer = response.getWriter();
writer.println(“Access Denied !! ” + authException.getMessage());
}
}

  • Implement interface AuthenticationEntryPoint
  • JwtAuthenticationEntryPoint is to handle unauthorized access attempts to your application.
  • When a user attempts to access a protected resource without proper authentication, this class sends an HTTP 401 (Unauthorized) response back to the client.
  • The response body contains a message indicating that access is denied, along with any relevant information from the AuthenticationException.

JWTHelper :

  • JwtHelper is a Spring component (@Component) that provides methods for working with JWTs in a Spring Security application.
  • It defines a constant JWT_TOKEN_VALIDITY to specify the validity duration of JWT tokens in seconds (e.g., 5 hours).
  • It uses a secret key (secret) for signing and verifying JWT tokens. This key should be kept secret.
  • getUsernameFromToken and getExpirationDateFromToken methods extract information (subject and expiration date) from a JWT token.
  • getClaimFromToken is a generic method to retrieve any claim from a JWT token using a function.
  • getAllClaimsFromToken parses and extracts all claims from a JWT token.
  • isTokenExpired checks if a JWT token has expired.
  • generateToken creates a JWT token for a user with specified UserDetails.
  • doGenerateToken generates a JWT token with claims, subject, issued date, expiration date, and signs it using the secret key.
  • validateToken checks if a JWT token is valid by comparing the username from the token with the UserDetails and checking for token expiration.
  • Overall, this class provides a convenient way to work with JWTs for user authentication and authorization in a Spring Security application. It’s used to generate tokens during login and validate tokens during subsequent requests to protected resources.

JWTAuthenticationFilter :

  • JwtAuthenticationFilter is a Spring component that extends OncePerRequestFilter, which ensures that the filter’s doFilterInternal method is executed only once per HTTP request.
  • Inside doFilterInternal, it extracts the “Authorization” header from the incoming HTTP request.
  • If the header starts with “Bearer,” it extracts the JWT token part.
  • It attempts to extract the username from the token using the JwtHelper class, handling various exceptions that may occur during token processing.
  • If a valid username is extracted and there is no existing authentication in the SecurityContextHolder, it fetches user details using the UserDetailsService.
  • It validates the token using the JwtHelper, and if validation is successful, it sets the authentication details in the SecurityContextHolder.
  • Finally, it continues processing the request by invoking the filterChain.doFilter method.
  • This filter essentially intercepts incoming requests, extracts JWT tokens, and performs user authentication based on the tokens, if applicable. It integrates JWT-based authentication into a Spring Security application.

Login Controller and SecurityConfig

Rest Controller for login authorization :

  • Create autowired instances of AuthManager, JWTHelper and UserDetailService
  • Have DTOs to handle JWTRequest and JWTResponse for login operation
  • doAuthenticate Method : This method performs authentication using Spring Security’s UsernamePasswordAuthenticationToken.
  • If authentication fails (e.g., due to bad credentials), a BadCredentialsException is thrown.
@PostMapping("/login")
    public ResponseEntity<JwtResponse> login(@RequestBody JwtRequest request) {

        this.doAuthenticate(request.getEmail(), request.getPassword());

        UserDetails userDetails = userDetailsService.loadUserByUsername(request.getEmail());
        String token = this.helper.generateToken(userDetails);

        JwtResponse response = JwtResponse.builder()
                .jwtToken(token)
                .username(userDetails.getUsername()).build();
        return new ResponseEntity<>(response, HttpStatus.OK);
    }

    private void doAuthenticate(String email, String password) {

        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(email, password);
        try {
            manager.authenticate(authentication);
        } catch (BadCredentialsException e) {
            throw new BadCredentialsException(" Invalid Username or Password  !!");
        }
    }

    @ExceptionHandler(BadCredentialsException.class)
    public String exceptionHandler() {
        return "Credentials Invalid !!";
    }

Create a security Config class :

  • Create 2 autowired beans : JwtAuthenticationEntryPoint & JwtAuthenticationFilter
  • private JwtAuthenticationEntryPoint point;: This line declares a private variable named point, which is an instance of a class called JwtAuthenticationEntryPoint. It seems to be used for handling authentication errors.
  • private JwtAuthenticationFilter filter;: This line injects an instance of the JwtAuthenticationFilter class into the current class. This filter is used for processing JWT (JSON Web Tokens) during authentication
  • The securityFilterChain method configures security settings for a web application using Spring Security
  • It disables CSRF protection, specifies that requests to “/test” require authentication while allowing unrestricted access to “/auth/login,” and mandates authentication for all other requests. It also defines how authentication errors should be handled using the JwtAuthenticationEntryPoint, sets the session creation policy as stateless to rely on JWT tokens, and adds the JwtAuthenticationFilter to process JWT tokens before the standard UsernamePasswordAuthenticationFilter. This method essentially establishes the security rules and components necessary for secure authentication and authorization in the application.

Databased backed Authentication

Update user entity

  • Implement UserDetails class and override all existing methods
  • update methods like isAccountNonExpired, NonLocked, etc as return true & getUsername to return the email
  • public class User implements UserDetails

Update SecurityConfig with doDAOAuthenticationProvider:

  • Autowire userDetailService and passwordEncoder to set them in DaoAuthProvider and return instance.

@Bean
public DaoAuthenticationProvider doDaoAuthenticationProvider(){
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailService);
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
return daoAuthenticationProvider;
}

Create CustomerUserDetailsService :

  • Implement UserDetailService class and override loadUserByUsername method to return User entity by accepting username (here email) in parameters and throw UsernameNotFoundException.

By Asif Raza

Leave Comment