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

  • Auth Service
Link to original

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 bits
  • ITERATIONS: 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:

  1. Prevents rainbow table attacks: Attackers can’t use precomputed hash lists.

  2. 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:

  1. Generate a random salt.

  2. Hash their password using PBKDF2 with:

    • Password
    • Salt
    • Iteration count
    • SHA-256
  3. Store the result:

    PBKDF2WithHmacSHA256$65536$<salt>$<hash>
    

When a user logs in:

  1. Extract algorithm, iterations, and salt from stored hash string.
  2. Re-hash the input password using the same parameters.
  3. 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.Service makes the controller to inject the service during its instantiation

TO DO

  • Get the jwt token
  • And Authorisation
  • Exception Handler