Search code examples
javaspringspring-securityjwt

How to Implement SecurityConfig in java spring for jwt token validation


I'm trying to implement Security Config for java spring project for JWT Token validation using spring security 6. Can anyone suggest the steps to implement it using bearer token.

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private JwtTokenFilter jwtTokenFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeHttpRequests()
                .requestMatchers("/api/authenticate").permitAll()
                .anyRequest().authenticated()
                .and()
                .sessionManagement(management -> management.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        // http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

Facing issues to setup configure.


Solution

  • How to Implement SecurityConfig in java spring for jwt token validation

    Here are steps to follow:

    1) Add all necessary dependencies to your project.

    2) Create a UserDetailsServiceImpl class that implements the UserDetailsService interface which is used to load user-specific data during authentication. This class has a method to retrieve the user's details based on the username or any other data passed in the request. It is possible to use any data source such as a database, a remote service to retrieve the user details.

    Here is the code example:

    import com.zufar.onlinestore.security.repository.UserDetailsRepository;
    
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.stereotype.Service;
    
    import lombok.RequiredArgsConstructor;
    
    @Service
    @RequiredArgsConstructor
    public class UserDetailsServiceImpl implements UserDetailsService {
        private final UserDetailsRepository userDetailsRepository;
    
        @Override
        public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
            return userDetailsRepository
                    .findByUsername(username)
                    .orElseThrow(() -> new UsernameNotFoundException("User doesn't exists"));
        }
    }
    

    3) Create a JwtTokenUtil class. This class is responsible for generating JWT tokens with a secret key and an expiration time, and also for validating JWT tokens passed in the HTTP header of incoming requests.

    Here is the code example:

    @Component
    public class JwtTokenUtil {
    
        private static final String SECRET_KEY = "your-secret-key";
        private static final long EXPIRATION_TIME = 89990000L;
    
        public String generateToken(UserDetails userDetails) {
            Map<String, Object> claims = new HashMap<>();
            return Jwts.builder()
                    .setClaims(claims)
                    .setSubject(userDetails.getUsername())
                    .setIssuedAt(new Date())
                    .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                    .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                    .compact();
        }
    
        public boolean validateToken(String token) {
            try {
                Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
                return true;
            } catch (JwtException e) {
                return false;
            }
        }
    
        public String getUsernameFromToken(String token) {
            return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getSubject();
        }
    }
    

    4) Create a class that implements Filter and extends OncePerRequestFilter. This filter is used to validate the JWT token in the incoming requests. This filter works this way:

    • The token is retrieved from the header of the HTTP request
    • The token is validated.
    • If the token is valid, the filter sets the authentication in the SecurityContextHolder.

    Here is the code example:

    import org.springframework.lang.NonNull;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.stereotype.Component;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import java.io.IOException;
    import java.util.Optional;
    
    import jakarta.servlet.FilterChain;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import lombok.RequiredArgsConstructor;
    
    @Component
    @RequiredArgsConstructor
    public class JwtAuthenticationFilter extends OncePerRequestFilter {
        private final JwtAuthenticationProvider jwtAuthenticationProvider;
    
        @Override
        protected void doFilterInternal(@NonNull final HttpServletRequest httpRequest,
                                        @NonNull final HttpServletResponse httpResponse,
                                        @NonNull final FilterChain filterChain) throws ServletException, IOException {
            Optional<UsernamePasswordAuthenticationToken> authenticationTokenOptional = jwtAuthenticationProvider.get(httpRequest);
    
            if (authenticationTokenOptional.isPresent()) {
                UsernamePasswordAuthenticationToken authenticationToken = authenticationTokenOptional.get();
                SecurityContextHolder
                        .getContext()
                        .setAuthentication(authenticationToken);
            }
    
            filterChain.doFilter(httpRequest, httpResponse);
        }
    }
    

    5) Create a SpringSecurityConfiguration class to configure the Spring Security filter chain. This way we can:

    • customize the authentication and authorization mechanisms,
    • specify the login and logout pages,
    • enable/disable CSRF protection,
    • and more.

    Here is the code example:

    import com.zufar.onlinestore.security.authentication.UserDetailsServiceImpl;
    import com.zufar.onlinestore.security.jwt.filter.JwtAuthenticationFilter;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    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;
    
    import lombok.RequiredArgsConstructor;
    
    @Configuration
    @EnableWebSecurity
    @RequiredArgsConstructor
    public class SpringSecurityConfiguration {
        private static final String API_AUTH_URL_PREFIX = "/api/auth/**";
        public static final String ACTUATOR_ENDPOINTS_URL_PREFIX = "/actuator/**";
    
        private final UserDetailsServiceImpl userDetailsService;
        private final JwtAuthenticationFilter jwtTokenFilter;
    
        @Bean
        public SecurityFilterChain securityFilterChain(final HttpSecurity httpSecurity) throws Exception {
            return httpSecurity
                    .csrf().disable()
                    .authorizeHttpRequests()
                    .requestMatchers(API_AUTH_URL_PREFIX).permitAll()
                    .requestMatchers(ACTUATOR_ENDPOINTS_URL_PREFIX).permitAll()
                    .anyRequest().authenticated()
                    .and()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
                    .build();
        }
    
        @Bean
        public AuthenticationProvider authenticationProvider() {
            DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
            authenticationProvider.setUserDetailsService(userDetailsService);
            authenticationProvider.setPasswordEncoder(passwordEncoder());
            return authenticationProvider;
        }
    
        @Bean
        public AuthenticationManager authenticationManager(final HttpSecurity httpSecurity,
                                                           final PasswordEncoder passwordEncoder,
                                                           final UserDetailsServiceImpl userDetailService) throws Exception {
            return httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
                    .userDetailsService(userDetailService)
                    .passwordEncoder(passwordEncoder)
                    .and()
                    .build();
        }
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    }