Como gerar um código de autenticidade dos dados?

Senhores, alguém de vocês teria uma sugestão de como fazer a geração de um código de autenticidade dos dados contidos em um objeto de forma elegante?

Exemplo:

@Data
public class User {
    public User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    private String firstName;
    private String lastName;
    private UUID authenticityCode;
}

A geração do authenticityCode seria baseada no hashCode gerado pela classe, conforme abaixo:

@UtilityClass
public class Utils {
    public static UUID generateAuthenticityCode(Object clazz) {
        ByteBuffer byteBuffer = ByteBuffer.allocate(Long.BYTES);
        return clazz == null ? null : UUID.nameUUIDFromBytes(byteBuffer.putLong(clazz.hashCode()).array());
    }
}

Desta forma que foi implementada, funcionaria da seguinte maneira:

public class Test {

    public static void main(String ... args) {
        User user = new User("John", "Doe");
        user.setAuthenticityCode(Utils.generateAuthenticityCode(user));

        System.out.println(user.getAuthenticityCode()); //Gera o seguinte valor: 24755a35-3457-38e4-9227-bb044eb78494
    }
}

A partir do momento em que essa informação é alterada este valor de authenticityCode também seria modificado pois a informação geraria outro hashCode, porém não consegui pensar em uma forma mais elegante e “automágica” de fazer isso, alguém tem sugestões?

Ja esta tao enxuta essa sua solucao. A unica coisa que poderia mudar seria trocar para MD5

Eu fiz uma outra implementação utilizando SHA-256 onde consigo obter o mesmo resultado esperado, porém eu queria tentar chegar em uma solução menos braçal, pois da forma que está implementado ele só gerará o código se eu realizar a chamada.

voce nao quer fazer essa chamada? entao faca essa chamada dentro do get

public String getAuthenticityCode() {
    return Utils.generateAuthenticityCode(this)
}
 System.out.println(user.getAuthenticityCode());

a outra solucao é implentar um metodo hashCode que devolta esse eu codigo.

Eu tentei utilizar desta forma, mas ele acaba gerando um StackOverflowError.

Gosto de usar uma abordagem com interfaces. Deixa o código mais fácil de fazer manutenção, evoluir, testar, etc.:

AuthenticityCodeGenerator

@FunctionalInterface
public interface AuthenticityCodeGenerator {
	String generate(DataAuthenticityCode data);
}

UUIDAuthenticityCodeGenerator

import java.nio.ByteBuffer;
import java.util.UUID;

public class UUIDAuthenticityCodeGenerator implements AuthenticityCodeGenerator {
	
	@Override
	public String generate(DataAuthenticityCode data) {
		ByteBuffer byteBuffer = ByteBuffer.allocate(Long.BYTES);
		return UUID.nameUUIDFromBytes(byteBuffer.putLong(data.hashCode()).array()).toString();
	}
}

Sha256AuthenticityCodeGenerator

import java.security.MessageDigest;

public class Sha256AuthenticityCodeGenerator implements AuthenticityCodeGenerator {
	
	@Override
	public String generate(DataAuthenticityCode data) {
		try {
			MessageDigest digest = MessageDigest.getInstance("SHA-256");
	        byte[] hash = digest.digest(String.valueOf(data.hashCode()).getBytes("UTF-8"));
	        StringBuffer hexString = new StringBuffer();
	
	        for (int i = 0; i < hash.length; i++) {
	            String hex = Integer.toHexString(0xff & hash[i]);
	            if(hex.length() == 1) hexString.append('0');
	            hexString.append(hex);
	        }
	
	        return hexString.toString();
		} catch(Exception ex) {
			return null;
		}
	}
}

DataAuthenticityCode

@FunctionalInterface
public interface DataAuthenticityCode {
	String generateAuthenticityCode(AuthenticityCodeGenerator generator);
}

User

import java.util.Objects;

public class User implements DataAuthenticityCode {
	
	private String firstName;
	private String lastName;
	
	public User(String firstName, String lastName) {
		this.firstName = firstName;
		this.lastName = lastName;
	}

	public String getFirstName() {
		return firstName;
	}

	public String getLastName() {
		return lastName;
	}
	
	public void changeLastname(String newLastname) {
		this.lastName = newLastname;
	}

	@Override
	public String generateAuthenticityCode(AuthenticityCodeGenerator generator) {
		return generator.generate(this);
	}

	@Override
	public int hashCode() {
		return Objects.hash(firstName, lastName);
	}
}

Main

public class DataAuthenticityMain {

	public static void main(String[] args) {
		AuthenticityCodeGenerator generator = new UUIDAuthenticityCodeGenerator();
		AuthenticityCodeGenerator sha256Generator = new Sha256AuthenticityCodeGenerator();

		User user = new User("John", "Does");
		System.out.println("UUID: " + user.generateAuthenticityCode(generator));
		System.out.println("sha256: " + user.generateAuthenticityCode(sha256Generator));
		

		user.changeLastname("Fulano");
		System.out.println("UUID: " + user.generateAuthenticityCode(generator));
		System.out.println("sha256: " + user.generateAuthenticityCode(sha256Generator));
	}
}

Coloquei a classe Sha256AuthenticityCodeGenerator apenas para exemplificar como seria mais simples adicionar um mecanismo de gerar hash usando interfaces (não é o foco do seu post, mas talvez seja bom olhar por esse lado tb).

Os nomes das classes ficaram meio confusos. Tenho certa dificuldade de nomear classes =/

1 curtida

Legal essa ideia com o uso de interfaces funcionais, fica bem legal a implementação, obrigado pela sugestão de solução! :smile: