To Do
- Understand Dependency Injection
- Add the DTO Layer
- Something called pojo classes are there
- Save the hashed password
- Make the auth handler
- Validate the user before allowing him the access
- Addition Of Roles into the System
I’m thinking to Start Working on the project
Priority
Link to original
- Auth Service
Today let’s make the password hasher and auth handler
Password Hashing Java
Cannot let the new backend developer get access of everyone’s password
🔐 What is password hashing?
Hashing is a one-way cryptographic function that transforms a password into a fixed-size, irreversible value (the hash). The idea is:
- You never store the actual password.
- You hash the password on registration and store only the hash.
- On login, you hash the user input again and compare it to the stored hash.
graph TD A[Password Sent By User] --> B[SHA 256 Taken of the password] B --> C[Hash & salt is stored in the db ]So let’s make a module
- Hashes Password
Code ins java
package in.abhi8290.helloworld.shared.util; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import java.security.SecureRandom; import java.util.Base64; import java.util.Objects; public class hashUtil { private static final int SALT_LENGTH = 16; // 128 bits private static final int HASH_LENGTH = 256; // bits private static final int ITERATIONS = 65536; private static final String ALGORITHM = "PBKDF2WithHmacSHA256"; // Generate a random salt public static byte[] generateSalt() { SecureRandom random = new SecureRandom(); byte[] salt = new byte[SALT_LENGTH]; random.nextBytes(salt); return salt; } // Hash the password with the given salt public static String hashPassword(String password , byte[] salt) throws Exception { PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, HASH_LENGTH); SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGORITHM); byte[] hash = factory.generateSecret(spec).getEncoded(); return Base64.getEncoder().encodeToString(hash); } public static String getHashedPasswordWithSaltAndAlgorithm(String password) throws Exception { byte[] salt = generateSalt(); return ALGORITHM + "$" + ITERATIONS + "$" + encodeSalt(salt) + "$" + hashPassword(password, salt); } public static boolean verifyPassword(String password, String hash) throws Exception { String[] splittedString = hash.split("\\$"); byte[] salt = Base64.getDecoder().decode(splittedString[2]); String savedHash = splittedString[3]; return Objects.equals(hashPassword(password, salt), savedHash); } // Encode salt to store it with the hash public static String encodeSalt(byte[] salt) { return Base64.getEncoder().encodeToString(salt); } // Decode salt when verifying public static byte[] decodeSalt(String encodedSalt) { return Base64.getDecoder().decode(encodedSalt); } }
Imports
import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import java.security.SecureRandom; import java.util.Base64;These are required for:
- Cryptographic hashing (
SecretKeyFactory,PBEKeySpec)- Secure salt generation (
SecureRandom)- Encoding/decoding salt and hash to/from Base64
Class and Constants
public class hashUtil { private static final int SALT_LENGTH = 16; // 128 bits private static final int HASH_LENGTH = 256; // bits private static final int ITERATIONS = 65536; private static final String ALGORITHM = "PBKDF2WithHmacSHA256";
SALT_LENGTH: Size of the random salt (16 bytes = 128 bits)HASH_LENGTH: Output hash size in bitsITERATIONS: Number of PBKDF2 iterations (makes hashing slower to resist brute force)ALGORITHM: Specifies the secure hash function
Method: generateSalt
public static byte[] generateSalt() { SecureRandom random = new SecureRandom(); byte[] salt = new byte[SALT_LENGTH]; random.nextBytes(salt); return salt; }Generates a cryptographically secure random salt.
Method: hashPassword
public static String hashPassword(String password) throws Exception { byte[] salt = generateSalt(); PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, HASH_LENGTH); SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGORITHM); byte[] hash = factory.generateSecret(spec).getEncoded(); return ALGORITHM + "$" + ITERATIONS + "$" + encodeSalt(salt) + "$" + Base64.getEncoder().encodeToString(hash); }
- Converts the password to a
char[](secure practice).- Uses the salt, iteration count, and desired hash length to configure
PBEKeySpec.- Generates the hash using
PBKDF2WithHmacSHA256.- Returns a single string:
algorithm$iterations$saltBase64$hashBase64
This encapsulates all data needed to later verify the password.
Method: encodeSalt
public static String encodeSalt(byte[] salt) { return Base64.getEncoder().encodeToString(salt); }Encodes the byte array salt to Base64 for easy storage.
Method: decodeSalt
public static byte[] decodeSalt(String encodedSalt) { return Base64.getDecoder().decode(encodedSalt); }Decodes a Base64-encoded salt string back to the original byte array (used during password verification).
🧂 What is a salt?
A salt is a random, unique value added to the password before hashing. It’s critical for two reasons:
Prevents rainbow table attacks: Attackers can’t use precomputed hash lists.
Ensures uniqueness: Even if two users have the same password, their hashes will be different because the salt is different.
In your code:
byte[] salt = generateSalt();Generates a 16-byte (128-bit) random salt per password.
🔁 What is PBKDF2?
PBKDF2 (Password-Based Key Derivation Function 2) is a standard for password hashing. It uses:
A base HMAC algorithm (like HmacSHA256),
A salt,
An iteration count (e.g. 65,536),
And a desired output length (e.g. 256 bits).
This makes brute-force attacks slower, because the hash takes more CPU time.
Your code:
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, HASH_LENGTH); SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGORITHM); byte[] hash = factory.generateSecret(spec).getEncoded();This means:
Password + Salt are passed through PBKDF2 with 65,536 iterations
The output is a 256-bit hash
Result is base64-encoded and stored
🧱 How it all fits together
When a user registers:
Generate a random salt.
Hash their password using PBKDF2 with:
- Password
- Salt
- Iteration count
- SHA-256
Store the result:
PBKDF2WithHmacSHA256$65536$<salt>$<hash>When a user logs in:
- Extract algorithm, iterations, and salt from stored hash string.
- Re-hash the input password using the same parameters.
- Compare the new hash to the stored hash (using constant-time comparison).
✅ Why this is secure
- Each password is uniquely hashed (thanks to the salt).
- Hashing is slow (iteration count), making brute-force infeasible.
- Algorithm (SHA-256) is cryptographically strong.
Let me know if you’d like a
verifyPassword()implementation added to your class.What are the ways people can get the password How to Name Things in Programming
Link to original
→ Okay So login api done
package in.abhi8290.helloworld.auth;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
import in.abhi8290.helloworld.user.User;
import in.abhi8290.helloworld.auth.dto.*;
@RestController
@RequestMapping("/auth")
public class AuthController {
public static final Logger logger = LoggerFactory.getLogger(AuthController.class);
private final AuthService authService;
public AuthController(AuthService authService) {
this.authService = authService;
}
@PostMapping("/login")
public Optional<User> login(@RequestBody LoginRequestDto request) throws Exception {
return authService.authenticate(request.getUsername(), request.getPassword());
}
}package in.abhi8290.helloworld.auth.dto;
public class LoginRequestDto {
String username;
String password;
public LoginRequestDto(String username, String password) {
this.username = username;
this.password = password;
}
public String getPassword() {
return password;
}
public String getUsername() {
return username;
}
}package in.abhi8290.helloworld.auth;
import in.abhi8290.helloworld.user.*;
import java.util.Optional;
import in.abhi8290.helloworld.shared.util.hashUtil;
import org.springframework.stereotype.Service;
@Service
public class AuthService {
public final UserService userService;
public AuthService(UserService userService) {
this.userService = userService;
}
public Optional<User> authenticate(String email, String password) throws Exception {
Optional<User> user = userService.findByEmail(email);
if (user.isEmpty()) throw new Exception("User Not Found");
boolean correctUser = hashUtil.verifyPassword(password, user.get().getPassword());
if(!correctUser) {
throw new Exception("Incorrect Password");
}
return user ;
}
}The
@Service : import org.springframework.stereotype.Servicemakes the controller to inject the service during its instantiation
TO DO
- Get the jwt token
- And Authorisation
- Exception Handler