java.lang.ClassCastException: [B无法转换为java.lang.String

9
public Login authenticate(Login login) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            String password = login.getPassword();
            try {
                md.update(password.getBytes("UTF-16"));
                byte[] digest = md.digest();
                String query = "SELECT L FROM Login AS L WHERE L.email=? AND L.password=?";
                Object[] parameters = { login.getEmail(), digest };
                List<Login> resultsList = (getHibernateTemplate().find(query,parameters));
                 if (resultsList.isEmpty()) {
                         //error dude
                     }
                 else if (resultsList.size() > 1) {
                         //throw expections
                     }
                 else {
                       Login login1 = (Login) resultsList.get(0);
                       return login1;
                 }
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }                
       return null;  
    }

异常

> java.lang.ClassCastException: [B
> cannot be cast to java.lang.String
>         at org.hibernate.type.StringType.toString(StringType.java:44)
>         at org.hibernate.type.NullableType.nullSafeToString(NullableType.java:93)
>         at org.hibernate.type.NullableType.nullSafeSet(NullableType.java:140)
>         at org.hibernate.type.NullableType.nullSafeSet(NullableType.java:116)
>         at org.hibernate.param.PositionalParameterSpecification.bind(PositionalParameterSpecification.java:39)
>         at org.hibernate.loader.hql.QueryLoader.bindParameterValues(QueryLoader.java:491)
>         at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1563)
>         at org.hibernate.loader.Loader.doQuery(Loader.java:673)
>         at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
>         at org.hibernate.loader.Loader.doList(Loader.java:2213)
>         at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2104)
>         at org.hibernate.loader.Loader.list(Loader.java:2099)
>         at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:378)
>         at org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:338)
>         at org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:172)
>         at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1121)
>         at org.hibernate.impl.QueryImpl.list(QueryImpl.java:79)
>         at org.springframework.orm.hibernate3.HibernateTemplate$29.doInHibernate(HibernateTemplate.java:856)
>         at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:373)
>         at org.springframework.orm.hibernate3.HibernateTemplate.find(HibernateTemplate.java:847)
>         at com.intermedix.services.LoginService.authenticate(LoginService.java:30)
>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native
> Method)
>         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
>         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
>         at java.lang.reflect.Method.invoke(Method.java:597)
>         at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:301)
>         at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182)
>         at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
>         at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
>         at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
>         at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
>         at $Proxy31.authenticate(Unknown Source)
>         at com.intermedix.ui.LoginDailog.checkLogin(LoginDailog.java:106)
>         at com.intermedix.ui.LoginDailog.access$0(LoginDailog.java:102)
>         at com.intermedix.ui.LoginDailog$2.handleAction(LoginDailog.java:88)
>         at com.vaadin.event.ActionManager.handleAction(ActionManager.java:228)
>         at com.vaadin.event.ActionManager.handleActions(ActionManager.java:198)
>         at com.vaadin.ui.Panel.changeVariables(Panel.java:345)
>         at com.vaadin.ui.Window.changeVariables(Window.java:1073)
>         at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.handleVariables(AbstractCommunicationManager.java:1094)
>         at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.doHandleUidlRequest(AbstractCommunicationManager.java:590)
>         at com.vaadin.terminal.gwt.server.CommunicationManager.handleUidlRequest(CommunicationManager.java:266)
>         at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:476)
>         at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
>         at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
>         at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:390)
>         at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
>         at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
>         at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
>         at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
>         at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
>         at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
>         at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
>         at org.mortbay.jetty.Server.handle(Server.java:326)
>         at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
>         at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:943)
>         at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:756)
>         at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
>         at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
>         at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
>         at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

2
你是否遇到编译器错误?警告?运行时异常? - stackr
我已经更新了问题本身,因为我得到了异常。 - theJava
3
错误似乎出现在该文件的第30行。哪一行是第30行? - Kinjal Dixit
4个回答

7

看起来你的数据库密码列是Java中映射为String类型(最可能是varchar)。因此,Hibernate无法将你的字节数组转换为String

你可以将代码改为:

 String digest = new String(md.digest());
 String query = "SELECT L FROM Login AS L WHERE L.email=? AND L.password=?";
 Object[] parameters = { login.getEmail(), digest };

但是,无论采用何种编码方式,摘要通常都包含无法映射为字符的字节,因此这种方法可能不起作用。你应该使用base64编码将二进制数据转换为字符串。
另一种解决方案是更改数据库模式,将password字段改为二进制而不是varchar
在两种情况下,你需要知道如何将password字段插入到数据库中。
关于你的代码,我有几点评论:
我认为通过使用用户名和密码从数据库中选择一行并检查密码有些奇怪。更合理的做法是仅使用用户进行选择,然后将提供的密码与数据库中返回的密码进行验证。
你使用哈希函数确保密码不以明文形式存储在数据库中,这很好。但是,你的方案存在一个缺陷:如果有多个用户具有相同的密码,则数据库中的哈希密码将相同。因此,如果你可以访问数据库并知道一个用户的密码,则很容易找到共享此密码的所有用户。为了构建更安全的系统,应使用包含一些salt的密码编码方案。

我不理解为什么在存储密码哈希时需要使用额外的盐。如果您不选择“安全性通过混淆”,那么您必须提供所有信息来重构哈希值,对吗?您能否给我一个更多关于这个建议的信息的链接? - mtraut
在这种情况下,盐的主要好处是确保散列密码的唯一性。 - gabuzo
在这种情况下,盐的主要好处是确保散列密码的唯一性。在散列密码时会随机选择盐,并将其附加到散列密码之前。您可以访问http://aspirine.org/htpasswd_en.html查看一个实现示例。它选择“MD5”作为算法,并对同一密码进行多次加密,您会发现每次都会生成不同的散列密码。在这个哈希方案中,盐是 $apr1$ 后面的字段。有关更多信息,请查看答案中的链接。 - gabuzo
我知道盐的作用——但我怀疑这是否会提高哈希密码存储的安全性,至少在你提到的情况下是如此。从计算上来看,相比于暴力攻击,一个一堆字节或先提取盐、重新哈希已知密码再进行比较并没有太大的区别。 - mtraut
是的,你说得很对。一切都取决于你想检查多少个已知密码。如果你有一个默认密码,并且想要检查一下是否有些用户忘记更改了它,盐值不会产生很大的影响。另一方面,如果你计划对一个拥有数百个用户的数据库进行字典攻击,盐值将产生显着的影响:一个需要两天的攻击将变成一个需要200天的攻击,如果有100个用户。 - gabuzo

2

看起来你在需要字符串的地方传入了一个字节数组。

尝试使用 { login.getEmail(), new String(digest) }; 替换 { login.getEmail(), digest };

参考 http://download.oracle.com/javase/1.4.2/docs/api/java/lang/String.html#String%28byte[]%29


无法工作,因为摘要肯定不包含任何字符字节序列。 - gabuzo
@Nishant 谢谢分享。new String("Any String") 这个解决了我的问题。给你一个加一。 - OO7

0

错误似乎在第30行,我猜测是Object[]参数行。如果是这样,您需要将byte[]摘要转换为字符串,并将该字符串用作参数。

此答案是在编辑问题之前给出的。

将事物封装在函数中:

byte[] digest = getMessageDigest(login.getPassword());
login1 = verifyPassword(login.getEmail(), digest);

当然相关的try/catch还在。

尝试摆脱使用e.printStackTrace()的习惯,改为使用java.utils.logging.Logger或log4j写入日志文件。

尽量让函数只有一个返回。如果你保持代码不变,在函数开头定义Login login1=null,并在else块中简单地赋值即可。最后的返回应该是return login1(可能是null或某个值)。

那些仍需实现的部分(例如错误提示)应该用//TODO:进行注释。大多数IDE(如eclipse/netbeans)会自动将这些注释识别为任务。


0
与此错误相关。 在我的情况下,我在测试中模拟了String类。
我有:
mockkStatic(Base64::class)
every { String(Base64.decode(text, 0)) } returns token

我把它改成了:

mockkStatic(Base64::class)
every { Base64.decode(text, 0) } returns token.toByteArray()

所以 String 类的行为符合需要。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接