【问题标题】:How to map a byte[] property with Hibernate?如何使用 Hibernate 映射 byte[] 属性?
【发布时间】:2012-05-14 21:02:12
【问题描述】:

我正在使用 Hibernate/Java 将实体持久保存到数据库中。该实体有一个 password 字段,它是一个 String。在我的应用程序中注册用户时,我使用 SHA-1 对密码进行哈希处理(我承认这有点弱)。这会产生一个 byte[] ,然后我将其转换为 String 使用 new String(byte[] arr); 每当我想登录用户时,我只需从数据库中检索散列密码(作为 String)并将其与登录时输入密码的摘要进行比较 hashedPasswordFromDatabase.equals(SHA1_HASH(inputPassword));

这在我的开发系统(Windows 7、JDK 1.6.0_23 / JDK 1.7、MySQL 5.5、Tomcat 6.0.26)上完美运行,但在我们的服务器上部署后(在 Linux 上运行 JDK 1.6 ),即使密码相同,equals 方法也永远不会计算为 TRUE。我快速设置了一个新的开发系统(Ubuntu 12.04、MySQL 5.5、JDK 1.7.0_03、Tomcat 7.0.22),但它也不能在那里工作。

我知道 String 类的 Java API 文档中所述的可能的编码问题,并且在 SO 的几个地方也有说明。我尝试了该论坛上建议的几种编码(例如 Base64、Latin-1),但最终得到了 UnsupportedEncodingException。我想我最好避免字符串转换。那么如何设计我的数据库,以便 Hibernate 生成的实体类为 password 字段提供 byte[] 而不是 String

【问题讨论】:

  • +1,非常好的问题。顺便说一句,这不是您问题的答案,我很幸运地使用了一些进出数据库的 Commons Base64 实用程序。
  • 为什么要存储代表数字的字符串而不是存储数字本身?
  • 不要使用一轮散列来保护密码。使用 PBKDF2 或 bcrypt 之类的方法进行 10 万轮 --- 即使 100k 也不是不合理的。直接存储固定长度的byte[] 对于更多数据库来说应该很容易,但您始终可以从字节数组创建BigInteger 并将其存储为数字类型。
  • @erickson 是的,不错。我打算在解决这个问题后升级密码保护的强度(刮掉 SHA-1、加盐、多重哈希等)。
  • 数据库中的数据类型是什么?

标签: java string hibernate encoding sha1


【解决方案1】:

是的,问题很可能出现在byte[]String 的转换中。您必须知道 SHA 生成原始的byte 数组,并且不保证任意的byte[] 将生成有效的String,与编码无关。因此,您的代码只是偶然工作。

通过以下方式完全避免问题:

  • 在 BLOB 中存储原始 byte[] - 最安全和最有效的存储方式。在 Hibernate 中,只需在您的 POJO 上使用 byte[] 属性。

  • 使用 编码byte[](查看Decode Base64 data in Java)并将其存储为字符串。

顺便说一句remember about salting!

【讨论】:

  • 非常感谢。你的意思是 BLOB 实际上会让 Hibernate 在实体中生成字段为byte[]
  • @SayoStealth-virusOladeji:如果你使用byte[],hbm2ddl 应该会生成 BLOB。反之亦然 - 如果您的数据库中有 BLOB,您可以安全地将其映射到 byte[]
  • 太棒了!我目前正在浏览您分享的 Base64 页面;值得一读。
【解决方案2】:

就我而言,糟糕的数据库设计促使我在 Clob 的情况下使用 Blob。解决方案是在休眠时映射带有 Lob 注释的属性,并将另一个属性放入 String 类型。

在其他级别的代码中,当我调用 get 或 set 时,我使用 String 属性和 this 属性,获取或设置字节数组值。

@Entity
@Table(name = "CMUN_TAGS")
@SequenceGenerator(name = "idSeqTag", sequenceName = "SEQ_CMUN_TAGS")
public class CmunTagsDO implements java.io.Serializable {
  private BigDecimal lngIdTag;
  private byte[] blobValTag;
  private String strValTag;

  @Id
  @Column(name = "LNG_ID_TAG", unique = true, nullable = false, precision = 20, scale = 0)
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "idSeqTag")
  public BigDecimal getLngIdTag() {
    return this.lngIdTag;
  }

  public void setLngIdTag(BigDecimal lngIdTag) {
    this.lngIdTag = lngIdTag;
  }

  @Column(name = "BLOB_VAL_TAG", nullable = false)
  @Lob
  public byte[] getBlobValTag() {
    return this.blobValTag;
  }

  public void setBlobValTag(byte[] blobValTag) {
    this.blobValorTag = blobValorTag;
  }

  @Transient
  public String getStrValTag() {
    strValTag = new String(getBlobValTag());
    return strValTag;
  }

  public void setStrValTag(String strValTag) {
    setBlobValTag(strValTag.getBytes());
    this.strValTag = strValTag;
  }
}

【讨论】:

    【解决方案3】:

    您可以像这样将字节转换为十六进制表示:

    public String encryptPassword(String passwordInClear) {
                // Salt all you want here.
                MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
        byte[] digest = sha256.digest(passwordInClear.getBytes());
        return digestToString(digest);
    }
    
    private String digestToString(byte[] digest) {
        StringBuilder hashString = new StringBuilder();
        for (int i = 0; i < digest.length; i++) {
            String hex = Integer.toHexString(digest[i]);
            if (hex.length() == 1) {
                hashString.append('0');
                hashString.append(hex.charAt(hex.length() - 1));
            } else {
                hashString.append(hex.substring(hex.length() - 2));
            }
        }
        return hashString.toString();
    }
    

    【讨论】:

    • 这对散列密码的强度有何影响?这种方法会削弱它吗?
    • @SayoStealth-virusOladeji 不,它不会削弱或加强它。这是一个简单的事情,通过用数字值表示每个字节(0 变为“00”,16 变为“10”,255 变为“FF”,等等),将一个字节映射到其“等效”字符串值。从一个传递到另一个是双向的,但真正的“优势”或“劣势”在于您对密码的消化。
    • 太棒了!非常感谢。我想我会采用这种方法,因为我不必接触数据库和任何其他接近它的东西:)
    猜你喜欢
    • 1970-01-01
    • 2011-06-22
    • 2011-02-28
    • 1970-01-01
    • 2017-09-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多