在这个问题上,我一直在撞墙。我读过类似的文章和文章;大多数建议在Tomcat的server.xml文件中将URIEncoding设置为UTF-8,但这似乎没有什么不同。
我已经将ReSTful Web服务部署到了在Tomcat 7上托管的测试环境中。尽管在该计算机上也安装了Java 7,但Tomcat配置为使用Java 6。当针对其中托管的服务运行基本身份验证测试时,登录失败,并且当原始凭据包含Unicode字符时,我收到HTTP状态401的响应。当凭据仅包含ASCII时,基本身份验证可以正常工作。我也可以完全不使用基本身份验证进行登录-我的服务支持自定义登录标头和RFC2047。使用这种方法,凭据是否包含Unicode都无关紧要,登录不成问题。
具体来说,“问题”似乎是用户名两次被UTF-8编码。我的记录器中有一个错误(单独的问题),其中的日志文件是ANSI编码的。当您将日志文件转换为UTF-8时,字符将正确显示。但是在这种情况下,有问题的用户名要长得多,当文件转换为UTF-8时,它看起来就应该像它最初那样(在转换之前)。例如:
不良(BASIC AUTH):SampleUser-ⰣŽ´eÌ‚é¾¾±->SampleUser-Â𣎴eÌ‚é¾±
良好(RFC 2047):SampleUser-¢ð£Ž´eÌ‚é¾±-> SampleUser-¢𣎴ê龱
真正的问题是,我有自己的Tomcat 7(Java 6)实例在本地运行,并且无法针对它重现该问题。我已经比较了两个Tomcat的conf目录,它们看起来是相同的。我无法弄清楚为什么基本身份验证在一种环境中有效而在另一种环境中无效。我正在从我的机器上运行测试,所以不会由于我的测试方式(JUnit / JSystem)不一致而导致。
这是我所知道的:
尊重的用户类型无关紧要
享有特权。用户名中的Unicode是有问题的因素。
请求是通过XML还是JSON发送都没有关系。我的服务支持两种类型的序列化。
在请求上,accept charset和content-type(如果适用)都设置为UTF-8。
两种环境中的Java系统属性都相同。
以下文章对我来说非常有趣,因为它们建议将RFC 2047和基本身份验证结合在一起的可能性。我认为这不是必需的,因为基本身份验证字符串本身仅包含ASCII(因为它是base-64编码的)。即使是这样,为什么在一台Tomcat服务器上而不是另一台Tomcat服务器上也需要这样的东西?我觉得采用这种组合方法并不能解决根本问题,这才是真正让我发疯的原因!
http://www.mentby.com/Group/tomcat-user/basic-authentication-failed-with-multibyte-username.html
What encoding should I use for HTTP Basic Authentication?
在此先感谢您对尝试或仔细检查的建议。测试环境在某种程度上仅限于我-我只能在下班时间“玩”它,所以如果我不及时做出回应,我会提前道歉。
最佳答案
从您提供的数据来看,实际上好像UTF-8数据已转换为ASCII编码,而不是双重UTF-8编码。
就实际问题而言,不幸的是基本身份验证没有提供任何方式来传输未解码的用户名和密码的字符集。因此,您的主要选择是假设并手动指定一个字符集,使用环境中的默认字符集,或确定提供该字符集的自定义方式(例如,另一个标头)。每种选项的类型取决于您对环境以及通信的客户端/服务器端拥有多少控制权,以及您是否希望所有调用都使用相同的字符集。
基于一台服务器正常运行而另一台服务器运行不正常,我假设解码当前正在使用环境中的默认字符集。您是正确的,因为编码后的字符串将仅包含ASCII(因此您可能看不到传输编码后的值的问题),因此在解码过程中(或之后)可能会丢失数据。根据您选择的库,它可能会生成一个字节数组或一个String,因此从字节数组创建String时,请确保检查您是否提供了字符集(例如new String(decodedData,someCharset))或看看是否有办法将其提供给库(如果它产生String的话)。