Spring Boot 3
JWTAuthenticationFilter
package com.expense_tracker.auth.config;
import com.expense_tracker.auth.service.JwtService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.lang.NonNull;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.HandlerExceptionResolver;
@Component
public class JWTAuthenticationFilter extends OncePerRequestFilter {
private JwtService jwtService;
private UserDetailsService userDetailsService;
private HandlerExceptionResolver handlerExceptionResolver;
public JWTAuthenticationFilter(
JwtService jwtService,
UserDetailsService userDetailsService,
HandlerExceptionResolver handlerExceptionResolver
) {
this.jwtService = jwtService;
this.userDetailsService = userDetailsService;
this.handlerExceptionResolver = handlerExceptionResolver;
}
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
try {
final String jwt = authHeader.substring(7);
final String userEmail = jwtService.extractUsername(jwt);
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (userEmail != null && authentication == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail);
if (jwtService.isTokenValid(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails.getUsername(),
null,
userDetails.getAuthorities()
);
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
filterChain.doFilter(request, response);
} catch (Exception exception) {
handlerExceptionResolver.resolveException(request, response, null, exception);
}
}
}
ApplicationConfiguration
package com.expense_tracker.auth.config;
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.configuration.AuthenticationConfiguration;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.expense_tracker.auth.repo.UserRepo;
import lombok.Builder;
@Configuration
@Builder
public class ApplicationConfiguration {
private UserRepo userRepo;
public ApplicationConfiguration(UserRepo userRepo) {
this.userRepo = userRepo;
}
@Bean
UserDetailsService userDetailsService() {
return username -> userRepo.findByEmail(username).orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
@Bean
BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
}
SecurityConfiguration
package com.expense_tracker.auth.config;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
private final AuthenticationProvider authenticationProvider;
private final JWTAuthenticationFilter jwtAuthenticationFilter;
public SecurityConfiguration(
JWTAuthenticationFilter jwtAuthenticationFilter,
AuthenticationProvider authenticationProvider
) {
this.authenticationProvider = authenticationProvider;
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.cors(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(requests -> requests
.requestMatchers("/auth/**").permitAll()
.anyRequest().authenticated())
.sessionManagement(sessionConfigure -> sessionConfigure.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.formLogin(AbstractHttpConfigurer::disable);
return http.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("http://localhost:8080"));
configuration.setAllowedMethods(List.of("GET","POST"));
configuration.setAllowedHeaders(List.of("Authorization","Content-Type"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",configuration);
return source;
}
}
AuthenticationController
package com.expense_tracker.auth.controller;
import java.io.IOException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.expense_tracker.auth.dto.LoginResponse;
import com.expense_tracker.auth.dto.RegisterUser;
import com.expense_tracker.auth.dto.UserLogin;
import com.expense_tracker.auth.entity.UserMst;
import com.expense_tracker.auth.service.AuthenticationService;
import com.expense_tracker.auth.service.JwtService;
import jakarta.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("/auth")
public class AuthenticationController {
private final JwtService jwtService;
private final AuthenticationService authenticationService;
public AuthenticationController(JwtService jwtService, AuthenticationService authenticationService) {
this.jwtService = jwtService;
this.authenticationService = authenticationService;
}
@PostMapping("/register")
public ResponseEntity<UserMst> register(@RequestBody RegisterUser registerUser, HttpServletRequest request) throws IOException {
UserMst registeredUser = authenticationService.signup(registerUser,request);
return ResponseEntity.ok(registeredUser);
}
@PostMapping("/login")
public ResponseEntity<LoginResponse> authenticate(@RequestBody UserLogin userLogin) {
UserMst authenticatedUser = authenticationService.authenticate(userLogin);
String jwtToken = jwtService.generateToken(authenticatedUser);
LoginResponse loginResponse = new LoginResponse();
loginResponse.setToken(jwtToken);
loginResponse.setExpiresIn(jwtService.getExpirationTime());
loginResponse.setUserId(authenticatedUser.getUserId());
return ResponseEntity.ok(loginResponse);
}
}
AuthenticationService
package com.expense_tracker.auth.service;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.expense_tracker.auth.dto.RegisterUser;
import com.expense_tracker.auth.dto.UserLogin;
import com.expense_tracker.auth.entity.UserMst;
import com.expense_tracker.auth.repo.UserRepo;
import jakarta.servlet.http.HttpServletRequest;
@Service
public class AuthenticationService {
private final UserRepo userRepo;
private final PasswordEncoder passwordEncoder;
private final AuthenticationManager authenticationManager;
public AuthenticationService(UserRepo userRepo, AuthenticationManager authenticationManager,
PasswordEncoder passwordEncoder) {
this.authenticationManager = authenticationManager;
this.userRepo = userRepo;
this.passwordEncoder = passwordEncoder;
}
public UserMst signup(RegisterUser input, HttpServletRequest request) throws IOException {
UserMst user = new UserMst();
user.setUserName(input.getUserName());
user.setEmail(input.getEmail());
user.setPassword(passwordEncoder.encode(input.getPassword()));
user.setDob(input.getDob());
user.setProfilePicId(input.getProfileId());
user.setStatus(1);
user.setCreatedByIp(request.getRemoteAddr());
user.setCreatedDate(new Date());
user = userRepo.save(user);
long sequence = user.getUserId();
return userRepo.save(user);
}
public UserMst authenticate(UserLogin input) {
authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(input.getEmail(), input.getPassword()));
return userRepo.findByEmail(input.getEmail()).orElseThrow();
}
}
JwtService
package com.expense_tracker.auth.service;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
@Service
public class JwtService {
private static final String SECRET_KEY = "dAJEVMAFChvUGIjRf3Rg5NiR2MjQpM+
OYUeU1iqkxEopP137ZGNjVPSGp6y04WtT";
private static long jwtExpiration = 3600000;
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
public String generateToken(UserDetails userDetails) {
return generateToken(new HashMap<>(), userDetails);
}
public String generateToken(Map<String, Object> extraClaims, UserDetails userDetails) {
return buildToken(extraClaims, userDetails, jwtExpiration);
}
public long getExpirationTime() {
return jwtExpiration;
}
private String buildToken(
Map<String, Object> extraClaims,
UserDetails userDetails,
long expiration
) {
return Jwts
.builder()
.setClaims(extraClaims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(getSignInKey(), SignatureAlgorithm.HS256)
.compact();
}
public boolean isTokenValid(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername())) && !isTokenExpired(token);
}
private boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
private Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
private Claims extractAllClaims(String token) {
return Jwts
.parserBuilder()
.setSigningKey(getSignInKey())
.build()
.parseClaimsJws(token)
.getBody();
}
private Key getSignInKey() {
byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
return Keys.hmacShaKeyFor(keyBytes);
}
}