设为首页 收藏本站
查看: 959|回复: 0

[经验分享] (转)深入理解tomcat处理编码的机制

[复制链接]

尚未签到

发表于 2015-8-9 10:41:57 | 显示全部楼层 |阅读模式
http://onexin.iyunv.com/source/plugin/onexin_bigdata/file:///C:/DOCUME%7E1/daixuan/LOCALS%7E1/Temp/moz-screenshot.png
  zz from http://hi.baidu.com/atell/blog/item/79af251d1d4d096df724e45b.html

  

本文从tomcat源码的鲜为人知的UDecoder类入手,试图讲解Tomcat内部处理编码的机制。只涉及且不完全涵盖Tomcat处理请求的编码机制,不涉及响应的编码机制。
1. 关于UDecoder
      查看tomcat5和tomcat6版本的源码,可以看到org.apache.tomcat.util.buf.UDecoder类。
这个类有什么特别的地方呢?     用简单的一句话概括UDecoder类做的事情:修改所输入的字节流,  将每3个以百分号%开头的字节(如“%HH”,占3个字节),转换成1个十六进制的字节码(如“0xHH”,占1个字节),且将加号"+"变成空格“ ”。
如图举例描述了6个以百分号%开头的字节“%D6%D0”变成了2个十六进制的字节码“[0xD6][0xD0]”。(注:我用中括号“[ ]”来表示其中的内容代表的是1个十六进制形式的字节,下同。)
http://hiphotos.baidu.com/atell/pic/item/a7c3ff36a0cb837d5ab5f5e7.jpg
     我们知道UDecoder类的功能后,进一步想知道的是,tomcat用这个类做了什么事情?
答案是:用于修改http请求中的表单参数的值,  将每1个“%HH”(占3个字节)变成“0xHH”(占1个字节),且将加号"+"(占1个字节)变成空格“ ”(占1个字节)。其中表单参数包括:get方式的url参数和post方式的 application/x-www-form-urlencoded的参数。
举个例子,当我们访问这样的url时,http://abc.com/?keywords=%C6%A1%BE%C6,UDecoder类会将“%C6%A1%BE%C6”转换成“[0xC6][0xA1][0xBE][0xC6]”。
第1点,我们了解UDecoder类就好,继续看第2点。
2. 关于tomcat的Character Encoding(参考 http://wiki.apache.org/tomcat/FAQ/CharacterEncoding)   
我们使用tomcat时知道,要用request.getParameter("keywords")来获取get或post的参数值,而当keywords不是ISO-8859-1编码的字符时,为了获取到正确的keywords值,我们知道有以下几种方式可以解决(假设参数以GBK编码):
第一种:自己编程实现转换:
String ISOkeywords = request.getParameter("keywords");//ISOkeywords是乱码      
String keywords = new String(ISOkeywords.getBytes("ISO-8859-1"), "GBK");//keywords=“啤酒”
第二种:设置${tomcat_home}/config/server.xml中的URIEncode配置项的值为“GBK”
第三种:设置${tomcat_home}/config/server.xml中的useBodyEncodingForURI配  置项的值为true,并实现一个SetCharacterEncodingFilter.java,在tomcat的目录  webapps/servlets-examples/WEB-INF/classes/filters  /SetCharacterEncodingFilter.java中存在这个范例。

3. 结合UDecoder和Character Encoding,分析访问http://abc.com/?keywords=%C6%A1%BE%C6时,tomcat处理编码的流程。
   tomcat的处理流程大致可以看作以下四个步骤(具体可参见本文最后的流程图):
  (1)访问http://abc.com/?keywords=%C6%A1%BE%C6
  (2)UDecoder将%C6%A1%BE%C6转换为[0xC6][0xA1][0xBE][0xC6],tomcat持有变量bytes=[0xC6][0xA1][0xBE][0xC6]。
  (3)如果设置了URIEncode配置项或者useBodyEncodingForURI配置项为“GBK”,则tomcat会做以下转换:
     String gbkKeywords = new String(bytes,"GBK");//bytes=[0xC6][0xA1][0xBE][0xC6],gbkKeywords=“啤酒”       程序员使用request.getParameter("keywords")得到的就是上面的gbkKeywords,因此是正确的关键字“啤酒”。如下图。   http://hiphotos.baidu.com/atell/pic/item/d46f1e158990e14fc83d6d85.jpg

(4)如果未设置了URIEncode配置项或者useBodyEncodingForURI配置项 ,则tomcat相当于执行了以下转换:
     String keywords = new String(bytes,"ISO-8859-1");//bytes=[0xC6][0xA1][0xBE][0xC6],keywords是乱码
     程序员使用request.getParameter("keywords")得到的就是上面的keywords,因此是乱码。如下图。     
   http://hiphotos.baidu.com/atell/pic/item/bc15c915c87c39474890a755.jpg
   所以,此时我们需要自己编程实现转换:      
   String isoKeywords = request.getParameter("keywords"); //isoKeywords是乱码
   String keywords = new String(isoKeywords.getBytes("ISO-8859-1"), "GBK");//keywords=“啤酒”
4. UDecoder带来的启发
  (1) 使用UDecoder,可以使得经过URLEncoding和未经过URLEncoding的请求都正确的得到解析。当然,前提是,未经过经过URLEncoding的URL编码和未经过URLEncoding的请求的字节码的编码是同样的一种编码,如都是GBK或UTF-8。
如http://search.china.alibaba.com/search/offer_search.htm?keywords=%C6%A1%BE%C6
和http://search.china.alibaba.com/search/offer_search.htm?keywords=[0xC6][0xA1][0xBE][0xC6],经过UDecoder的处理后,keywords都会变成[0xC6][0xA1][0xBE][0xC6],随后流程和第3点说的流程是一致的。
    想象一下,假如没有UDecoder,那么如果要正确处理keywords=%C6%A1%BE%C6和keywords=[0xC6][0xA1][0xBE][0xC6],tomcat就必须分2种情况去处理:
    当keywords=%C6%A1%BE%C6时,tomcat获得字符串“%C6%A1%BE%C6”,然后使用URLDecoder.decode("%C6%A1%BE%C6")得到“啤酒”;
    当keywords=[0xC6][0xA1][0xBE][0xC6]时,tomcat直接获取字节码bytes=[0xC6][0xA1][0xBE][0xC6],然后使用new String(bytes, "GBK");得到“啤酒”;
   这样远远没有UDecoder的实现那么美观和简单。
20101021补充,总结一下,在写Servlet时,应该怎么做:
  如果URIEncoding=“ISO-8859-1”,而浏览器的keywords可能是中文或urlencoding的,好像这样也行:
String keywords = request.getParameter("keywords");//"????"或"%C6%A1%BE%C6"
String keywords2 = new String(keywords.getBytes("ISO-8859-1"),"GBK");//“中文”或"%C6%A1%BE%C6"
String keywords3 = URLDecoder.decode(keywords2,"GBK");//“中文”
  如果未知URIEncoding=“GBK”还是“ISO-8859-1”,已知浏览器的keywords是urlencoding的:
String keywords = request.getParameter("keywords");//"中文"或"%C6%A1%BE%C6"
String keywords2 = URLDecoder.decode(keywords2,"GBK");//“中文”
  如果未知URIEncoding=“GBK”还是“ISO-8859-1”,已知浏览器的keywords是中文的:
String keywords = request.getParameter("keywords");//"中文"或"???"
String keywords2 = new String(keywords.getBytes("ISO-8859-1"),"GBK");//则不行!
  如果URIEncoding=“GBK”,而浏览器的keywords可能是中文或urlencoding的:
String keywords = request.getParameter("keywords");//“中文” 这样即可。
另外,其实java.net.URLDecoder和UDecoder是类似的,不过URLDecoder是缺陷的。URLDecoder.decode的参数是String,它通过
str.charAt()来解码%HH->[0xHH],然后new String(bytes, 0, pos, enc).而UDecoder.decode()
的参数是字节数组。区别看出来了吗? URLDecoder.decode("???")时,charAt是什么,是“?”,结果还是
URLDecoder.decode("???")="???". 而UDecoder.decode(字节数组)是可以正确解码的。
(2) 为了SEO,有些网站会将参数放到pathInfo中,对于pathInfo的参数解析,我们也可以模仿UDecoder机制。据说前段时间百度爬虫访问阿里巴巴时使用的url就是未URLEncoding的(例如http://search.china.alibaba.com/selloffer/啤酒.html,啤酒实际是GBK的字节码[0xC6][0xA1][0xBE][0xC6]),由于我们对未URLEncoding的pathInfo参数无法正确解析,这样导致百度搜索结果中阿里巴巴的条目有乱码。百度的做法是不规范的,但或许使用UDecoder机制去处理pathInfo,就可以避免这种情况。
5. 最后附上一张图,作为总结。
http://hiphotos.baidu.com/atell/pic/item/cae89d92b03b0ccba877a40f.jpg

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-96304-1-1.html 上篇帖子: Tomcat中的docBase配置(映射非webapps目录下的项目) 下篇帖子: eclipse(myeclipse)tomcat插件,加载不了工程,空启动,如何解决
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表