Global Exception Handling in Spring Boot Applications
In production applications, unhandled exceptions can expose internal system details to users. Implementing global exception handling ensures consistent error responses and improves user experience.
Creating a Global Exception Handler
Create an exception handling component in your common module:
package com.example.common.exception;
import com.example.model.common.dtos.ApiResponse;
import com.example.model.common.enums.ErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public ApiResponse handleGenericException(Exception ex) {
log.error("System exception occurred: {}", ex.getMessage());
return ApiResponse.error(ErrorCode.SERVER_ERROR);
}
}
The @ControllerAdvice annotation enables global exception handling across all controllers, while @ExceptionHandler specifies which exceptions to catch.
Configuring Exception Handling
Enable the exception handler in your application configuration:
package com.example.admin.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.example.common.exception")
public class ExceptionConfig {
}
Password Security Implementation
Secure Password Storage
Never store passwords in plain text. Use secure hashing algorithms with salt:
import org.springframework.util.DigestUtils;
public class PasswordUtil {
public static String hashPassword(String password, String salt) {
String combined = password + salt;
return DigestUtils.md5DigestAsHex(combined.getBytes());
}
public static boolean verifyPassword(String input, String storedHash, String salt) {
String hashedInput = hashPassword(input, salt);
return hashedInput.equals(storedHash);
}
}
BCrypt Password Encoding
For enhanced security, use BCrypt password encoding:
import org.springframework.security.crypto.bcrypt.BCrypt;
public class BCryptUtil {
public static String encodePassword(String plainPassword) {
String salt = BCrypt.gensalt();
return BCrypt.hashpw(plainPassword, salt);
}
public static boolean matches(String plainPassword, String encodedPassword) {
return BCrypt.checkpw(plainPassword, encodedPassword);
}
}
JWT Token Implementation
JWT Token Generation
Create JWT tokens for authentication:
package com.example.utils.security;
import io.jsonwebtoken.*;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;
public class JwtTokenUtil {
private static final long TOKEN_EXPIRATION = 3600 * 1000;
private static final String SECRET_KEY = "YourSecretKeyHere";
public static String generateToken(Long userId) {
Map<string object=""> claims = new HashMap<>();
claims.put("userId", userId);
return Jwts.builder()
.setId(UUID.randomUUID().toString())
.setIssuedAt(new Date())
.setSubject("authentication")
.addClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + TOKEN_EXPIRATION))
.signWith(SignatureAlgorithm.HS512, generateKey())
.compact();
}
public static Claims validateToken(String token) {
return Jwts.parser()
.setSigningKey(generateKey())
.parseClaimsJws(token)
.getBody();
}
private static SecretKey generateKey() {
byte[] encodedKey = Base64.getEncoder().encode(SECRET_KEY.getBytes());
return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
}
}
</string>
Admin Authentication Service
User Authentication Implementation
package com.example.admin.service;
import com.example.model.admin.dtos.LoginRequest;
import com.example.model.admin.pojos.AdminUser;
import com.example.model.common.dtos.ApiResponse;
import com.example.utils.security.JwtTokenUtil;
import org.springframework.util.DigestUtils;
@Service
public class AuthenticationService {
@Autowired
private AdminUserRepository userRepository;
public ApiResponse authenticate(LoginRequest request) {
AdminUser user = userRepository.findByUsername(request.getUsername());
if (user == null) {
return ApiResponse.error("User not found");
}
String hashedInput = DigestUtils.md5DigestAsHex(
(request.getPassword() + user.getSalt()).getBytes());
if (user.getPasswordHash().equals(hashedInput)) {
String token = JwtTokenUtil.generateToken(user.getId());
Map<string object=""> responseData = new HashMap<>();
responseData.put("token", token);
responseData.put("user", user.withoutSensitiveData());
return ApiResponse.success(responseData);
} else {
return ApiResponse.error("Invalid credentials");
}
}
}
</string>
Authentication Controller
package com.example.admin.controller;
@RestController
@RequestMapping("/auth")
public class AuthController {
@Autowired
private AuthenticationService authService;
@PostMapping("/login")
public ApiResponse login(@RequestBody LoginRequest request) {
return authService.authenticate(request);
}
}