如何加密和解密列值在nativeQueries和JPA中使用它?

Rya*_*yan 8 java mysql encryption hibernate jpa

我需要加密 MySQL 数据库表中的值:

用户

| userId |      email   | userAge | firstName| lastName  | userType |
|--------|--------------|---------|-----------|----------|--------- |
|    1   |john@gmail.com| 20      | John      | Smith    | 1        |
Run Code Online (Sandbox Code Playgroud)

我的用户类别如下

 @Entity
 @Table(name = "user")
 public class User{

 @Id @GeneratedValue
 @Column(name = "userId ")
 private int userId;
 
 @Column(name = "firstName")
 private String firstName;
 
 @Column(name = "email")
 private String email;
 
 @Column(name = "lastName")
 private String lastName;

 @Column(name = "userType ")
 private String userType ;

 // getters and setters
 }
Run Code Online (Sandbox Code Playgroud)

我尝试使用 AttributeConverter 来做到这一点

@Component
public class ValueAttributeConverter implements AttributeConverter<String, String> {

private static final String AES = "AES";
private static final String SECRET = "secretkey";

private final Key key;
private final Cipher cipher;

public AttributeEncryptor() throws Exception {
    key = new SecretKeySpec(SECRET.getBytes(), AES);
    cipher = Cipher.getInstance(AES);
}

@Override
public String convertToDatabaseColumn(String value) {
    try {
        cipher.init(Cipher.ENCRYPT_MODE, key);
                                                                                
   return Base64.getEncoder().encodeToString(cipher.doFinal(value.getBytes()));
    } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException e) 
    {
        throw new IllegalStateException(e);
      }
     }

@Override
public String convertToEntityAttribute(String value) {
    try {
        cipher.init(Cipher.DECRYPT_MODE, key);
        return new String(cipher.doFinal(Base64.getDecoder().decode(value)));
    } catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException e{
        throw new IllegalStateException(e);
      }
      }     
      }
Run Code Online (Sandbox Code Playgroud)

并在属性上添加@Convert注解

@Column
@Convert(converter = ValueAttributeConverter.class)
private String email;
Run Code Online (Sandbox Code Playgroud)

这将加密列值。

但问题是我在 UserRepository 中使用了本机查询

 @Query(nativeQuery = true , value = "select * from users where email=?1 
  and 
 firstName =?2")
Run Code Online (Sandbox Code Playgroud)

在这种情况下,它不起作用,因为电子邮件值已加密。如果我使用其他列而不是电子邮件,例如姓氏。它正在工作并返回解密的值。

我尝试在查询中起诉字符串之前对其进行加密

 email = valueAttributeConverter.convertToEntityAttribute(email);

 
Run Code Online (Sandbox Code Playgroud)

它有效,但我不知道这种方法是否好。

我也尝试过使用@ColumnTransformer

  @Column
  @ColumnTransformer(
      read = "cast(aes_decrypt(email, 'secretkey') as char(255))", 
      write = "aes_encrypt(?, 'secretkey')"
       ) 
     private String email;
Run Code Online (Sandbox Code Playgroud)

这在加密时也工作正常,但在我需要解密时就不起作用。即使我选择使用另一列

 @Query(nativeQuery = true , value = "select * from users where userId 
   =?1")
Run Code Online (Sandbox Code Playgroud)

它不会解密该值,而是返回加密的值。

我的问题是,是否有更好的方法可以解决此加密和解密问题,并在 JPA 和 nativeQuery 中使用它?

D-F*_*ENS 1

我认为您应该重新考虑在代码中使用本机查询。您可以查看使用数据访问对象模式:https ://www.baeldung.com/java-dao-pattern

使用本机查询应该是最后的手段,并且仅在您绝对需要某些特定于平台的功能的情况下使用。

从架构上讲,您可以保持 User 实体不变,但在数据访问对象中用本机查询替换JPQL查询,例如本机查询示例

@Query(nativeQuery = true , value = "select * from users where userId =?1")
Run Code Online (Sandbox Code Playgroud)

将转换为 DAO 方法:

@Query(nativeQuery = true , value = "select * from users where userId =?1")
Run Code Online (Sandbox Code Playgroud)

DAO 模式允许您使用业务逻辑创建任意复杂的查询(就像您的情况一样),并为您提供更多控制。如果您使用示例中的 AttributeConverter,JPA 应该在返回的 User 对象中自动处理加密值。

JPQL 的优点在于它不是本机 SQL,而是由 JPA 解释,然后自动生成特定于平台的查询。您的代码可以在 JPA 支持的任何数据库后端上运行。此外,您还可以免费获得 SQL 注入保护等安全优势。