การเข้ารหัสและถอดรหัส
ผ่านรูปต่อไปนี้เราสามารถเข้าใจว่ามีการแปลงรหัสใน Javaweb:
ผู้ใช้ต้องการให้เซิร์ฟเวอร์ส่งคำขอ HTTP สถานที่ที่การเข้ารหัสเป็น URL, คุกกี้และพารามิเตอร์ หลังจากการเข้ารหัสเซิร์ฟเวอร์ยอมรับคำขอ HTTP แยกวิเคราะห์คำขอ HTTP จากนั้นถอดรหัส URL, คุกกี้และพารามิเตอร์ ในระหว่างการประมวลผลตรรกะทางธุรกิจของเซิร์ฟเวอร์อาจจำเป็นต้องอ่านฐานข้อมูลไฟล์ท้องถิ่นหรือไฟล์อื่น ๆ ในเครือข่าย ฯลฯ และกระบวนการเหล่านี้จำเป็นต้องมีการเข้ารหัสและการถอดรหัส หลังจากการประมวลผลเสร็จสิ้นเซิร์ฟเวอร์จะเข้ารหัสข้อมูลและส่งไปยังไคลเอนต์และเบราว์เซอร์จะแสดงไปยังผู้ใช้หลังจากถอดรหัส มีการเข้ารหัสและการถอดรหัสจำนวนมากที่เกี่ยวข้องในกระบวนการทั้งหมดนี้และสถานที่ที่น่าจะปรากฏตัวเป็นไปไม่ได้คือกระบวนการโต้ตอบกับเซิร์ฟเวอร์และไคลเอนต์
กระบวนการทั้งหมดข้างต้นสามารถสรุปได้ดังนี้: ข้อมูลที่เข้ารหัสหน้าถูกส่งผ่านไปยังเซิร์ฟเวอร์และเซิร์ฟเวอร์ถอดรหัสข้อมูลที่ได้รับและหลังจากการประมวลผลตรรกะทางธุรกิจบางอย่างผลลัพธ์สุดท้ายจะถูกเข้ารหัสและประมวลผลและลูกค้าถอดรหัสและแสดงต่อผู้ใช้ ด้านล่างฉันจะขอคำอธิบายเกี่ยวกับการเข้ารหัสและถอดรหัส Javaweb
หากลูกค้าต้องการให้เซิร์ฟเวอร์ส่งคำขอจะผ่านสี่สถานการณ์:
1. การเข้าถึงโดยตรงโดย URL
2. ลิงค์หน้า
3. แบบฟอร์มรับการส่ง
4. การส่งโพสต์แบบฟอร์ม
วิธี URL: สำหรับ URL หาก URL ทั้งหมดเป็นภาษาอังกฤษไม่มีปัญหา หากมีภาษาจีนการเข้ารหัสจะเกี่ยวข้อง วิธีการเข้ารหัส? คุณต้องการเข้ารหัสกฎอะไรบ้าง? แล้วจะถอดรหัสได้อย่างไร? คำตอบจะได้รับคำตอบทีละด้านด้านล่าง! ก่อนอื่นให้ดูที่ส่วนประกอบของ URL:
ใน URL นี้เบราว์เซอร์จะเข้ารหัสเส้นทางและพารามิเตอร์ เพื่ออธิบายกระบวนการเข้ารหัสให้ดีขึ้นให้ใช้ URL ต่อไปนี้
http://127.0.0.1:8080/perbank/i am cm? name = i am cm
ป้อนที่อยู่ด้านบนลงในกล่องอินพุต URL ของเบราว์เซอร์ โดยการดูข้อมูลส่วนหัวข้อความ HTTP เราสามารถดูว่าเบราว์เซอร์เข้ารหัสได้อย่างไร นี่คือเงื่อนไขการเข้ารหัสของสามเบราว์เซอร์:
คุณจะเห็นได้ว่าการเข้ารหัสของ "ฉัน" โดยเบราว์เซอร์ใหญ่มีดังนี้:
ส่วนเส้นทาง | สตริงแบบสอบถาม | |
Firefox | E6 88 91 E6 98 AF | E6 88 91 E6 98 AF |
โครเมี่ยม | E6 88 91 E6 98 AF | E6 88 91 E6 98 AF |
เช่น | E6 88 91 E6 98 AF | CE D2 CA C7 |
void converturi ที่ได้รับการป้องกัน (MessageBytes URI, คำขอคำขอ) โยนข้อยกเว้น {Bytechunk bc = uri.getBytechunk (); ความยาว int = bc.getLength (); Charchunk CC = uri.getcharchunk (); CC.Allocate (ความยาว, -1); enc = connector.geturiencoding (); // รับชุดการถอดรหัส URI ถ้า (enc! = null) {b2cconverter conv = request.geturiconverter (); ลอง {ถ้า (conv == null) {conv = new b2cconverter (enc); request.seturiconverter (Conv); }} catch (ioexception e) {... } ถ้า (conv! = null) {ลอง {conv.convert (bc, cc, cc.getBuffer (). ความยาว - cc.getend ()); uri.setchars (cc.getBuffer (), cc.getStart (), cc.getLength ()); กลับ; } catch (ioexception e) {... }}} // การเข้ารหัสเริ่มต้น: ไบต์การแปลงเร็ว [] bbuf = bc.getBuffer (); ถ่าน [] cbuf = cc.getBuffer (); int start = bc.getStart (); สำหรับ (int i = 0; i <length; i ++) {cbuf [i] = (char) (bbuf [i+start] & 0xff); } uri.setchars (cbuf, 0, ความยาว); - จากรหัสข้างต้นเราจะเห็นว่าการดำเนินการถอดรหัสของ URI คือการได้รับชุดการถอดรหัสของตัวเชื่อมต่อซึ่งกำหนดค่าใน server.xml ก่อน
<ตัวเชื่อมต่อ uriencoding = "UTF-8" />
หากไม่ได้กำหนดไว้การเข้ารหัสเริ่มต้น ISO-8859-1 จะถูกใช้สำหรับการแยกวิเคราะห์
สำหรับส่วนสตริงแบบสอบถามเรารู้ว่าไม่ว่าเราจะส่งผ่าน Get หรือ Post พารามิเตอร์ทั้งหมดจะถูกบันทึกไว้ในพารามิเตอร์และจากนั้นเราใช้ Request.getParameter งานถอดรหัสจะเสร็จสิ้นเมื่อวิธีการ getParameter เรียกว่าครั้งแรก ภายในวิธี getParameter มันเรียกว่าวิธีการ parseparameters ของ org.apache.catalina.connector.request ซึ่งจะถอดรหัสพารามิเตอร์ที่ผ่าน รหัสต่อไปนี้เป็นเพียงส่วนหนึ่งของวิธี Parseparameters:
// รับสตริงการเข้ารหัส enc = getCharacterencoding (); // รับ boolean charset ที่กำหนดไว้ใน ContentType useBodyEncodingForuri = connector.getUseBodyEncodingForuri (); if (enc! = null) {// ถ้าการเข้ารหัสไม่ว่างให้ตั้งค่าการเข้ารหัสเป็นพารามิเตอร์ ENC.SetEncoding (ENC); if (useBodyEncodingForuri) {// ถ้าตั้งค่าชาร์ตตั้งค่าให้ตั้งค่าการถอดรหัสการสืบค้นเป็นพารามิเตอร์ ChartSet.SetQueryStringEncoding (ENC); }} else {// ตั้งค่าพารามิเตอร์วิธีการถอดรหัสเริ่มต้นการตั้งค่า (org.apache.coyote.constants.default_character_encoding); if (useBodyenCodingForuri) {parameters.SetQueryStringEncoding (org.apache.coyote.constants.default_character_encoding); - จากรหัสด้านบนเราจะเห็นว่ารูปแบบการถอดรหัสของสตริงการสืบค้นนั้นใช้ชาร์ตเซ็ตตั้งค่าหรือใช้รูปแบบการถอดรหัสเริ่มต้น ISO-8859-1 โปรดทราบว่า Chartset ในการตั้งค่านี้เป็น contentType ที่กำหนดไว้ในส่วนหัว HTTP ในเวลาเดียวกันหากเราต้องการเปลี่ยนแอตทริบิวต์ที่ระบุเพื่อให้มีผลเราจำเป็นต้องกำหนดค่าต่อไปนี้:
<connector uriencoding = "UTF-8" useBodyenCodingForuri = "true"/>>>> >>
ส่วนข้างต้นแนะนำกระบวนการเข้ารหัสและถอดรหัสของคำขอ URL ในรายละเอียด ในความเป็นจริงสำหรับเราวิธีอื่น ๆ ของเราคือการส่งในแบบฟอร์ม
แบบฟอร์มได้รับ
เรารู้ว่าการส่งข้อมูลผ่าน URL นั้นง่ายที่จะทำให้เกิดปัญหารหัสที่อ่านไม่ออกดังนั้นเราจึงมักจะใช้แบบฟอร์มแบบฟอร์ม เมื่อผู้ใช้คลิกส่งแบบฟอร์มเบราว์เซอร์จะตั้งรหัสเพิ่มเติมเพื่อส่งข้อมูลไปยังเซิร์ฟเวอร์ ข้อมูลที่ส่งผ่าน Get นั้นถูกประกบกันหลังจาก URL (สามารถถือได้ว่าเป็นสตริงการสืบค้น ??) ดังนั้นการเรียนรู้ด้วย uriencoding มีบทบาทในกระบวนการถอดรหัสของเซิร์ฟเวอร์ Tomcat เซิร์ฟเวอร์ Tomcat จะถอดรหัสตามชุด Uriencoding และหากไม่ได้ตั้งค่าจะใช้ ISO-8859-1 เริ่มต้นเพื่อถอดรหัส หากเราตั้งค่าการเข้ารหัสเป็น UTF-8 บนหน้าและการเข้ารหัส uriencoding ไม่ได้หรือไม่ได้ตั้งค่ารหัสที่อ่านไม่ออกจะเกิดขึ้นเมื่อเซิร์ฟเวอร์ถอดรหัส ในเวลานี้เราสามารถรับข้อมูลที่ถูกต้องผ่านรูปแบบของสตริงใหม่ (request.getParameter ("ชื่อ") GetBytes ("ISO-8859-1"), "UTF-8")
รูปแบบโพสต์
สำหรับวิธีการโพสต์การเข้ารหัสที่ใช้นั้นจะถูกกำหนดโดยหน้านั่นคือ ContentType เมื่อฉันส่งแบบฟอร์มโดยคลิกที่ปุ่มส่งบนหน้าเบราว์เซอร์จะเข้ารหัสพารามิเตอร์ของแบบฟอร์มโพสต์ก่อนตามรูปแบบการเข้ารหัส Charset ของ OntentType และส่งไปยังเซิร์ฟเวอร์ ทางด้านเซิร์ฟเวอร์มันยังใช้ชุดอักขระที่ตั้งไว้ใน ContentType เพื่อถอดรหัส (แตกต่างจากวิธี GET ที่นี่) ซึ่งหมายความว่าพารามิเตอร์ที่ส่งผ่านแบบฟอร์มโพสต์โดยทั่วไปไม่มีปัญหาที่อ่านไม่ออก แน่นอนเราสามารถตั้งค่าชุดอักขระที่เข้ารหัสด้วยตนเอง: request.Setcharacterencoding (charset)
แก้ปัญหา URL ที่อ่านไม่ออกเป็นภาษาจีน
เราส่งคำขอไปยังเซิร์ฟเวอร์เป็นหลักผ่านการส่งสองรูปแบบ: URL และแบบฟอร์ม แบบฟอร์มโดยทั่วไปไม่มีปัญหาที่อ่านไม่ออกและปัญหาที่อ่านไม่ออกส่วนใหญ่อยู่ใน URL ผ่านการแนะนำบล็อกก่อนหน้านี้เรารู้ว่ากระบวนการส่งคำขอการเข้ารหัสไปยังเซิร์ฟเวอร์โดย URL นั้นทำให้เกิดความสับสนมากเกินไป ระบบปฏิบัติการที่แตกต่างกันเบราว์เซอร์ที่แตกต่างกันและชุดอักขระเว็บที่แตกต่างกันจะนำไปสู่ผลลัพธ์การเข้ารหัสที่แตกต่างกันโดยสิ้นเชิง มันน่ากลัวเกินไปหรือไม่ถ้าโปรแกรมเมอร์ต้องการคำนึงถึงผลลัพธ์ทุกอย่าง มีวิธีที่จะทำให้แน่ใจว่าไคลเอนต์ใช้วิธีการเข้ารหัสเพียงวิธีเดียวเพื่อออกคำขอไปยังเซิร์ฟเวอร์หรือไม่?
มี! ที่นี่ส่วนใหญ่ฉันให้วิธีการต่อไปนี้
จาวาสคริปต์
การใช้การเข้ารหัส JavaScript ไม่ได้ทำให้เบราว์เซอร์มีโอกาสเข้าแทรกแซง หลังจากเข้ารหัสแล้วให้ส่งคำขอไปยังเซิร์ฟเวอร์แล้วถอดรหัสในเซิร์ฟเวอร์ เมื่อเรียนรู้วิธีนี้เราต้องการสามวิธีในการเข้ารหัส JavaScript: Escape (), encodeuri () และ encodeuricomponent ()
หนี
สตริงที่ระบุจะถูกเข้ารหัสโดยใช้ชุดอักขระละติน SIO อักขระที่ไม่ใช่ ASCII ทั้งหมดจะถูกเข้ารหัสเป็นสตริงในรูปแบบ %XX โดยที่ XX แสดงถึงหมายเลขเลขฐานสิบหกที่สอดคล้องกับอักขระในชุดอักขระ ตัวอย่างเช่นการเข้ารหัสที่สอดคล้องกับรูปแบบคือ %20 วิธีการถอดรหัสที่สอดคล้องกันคือ Unsescape ()
ในความเป็นจริง Escape () ไม่สามารถใช้โดยตรงสำหรับการเข้ารหัส URL ฟังก์ชั่นจริงของมันคือการส่งคืนค่า Unicode ที่เข้ารหัสของตัวละคร ตัวอย่างเช่นผลลัพธ์ของ "ฉันเป็น CM" ด้านบนคือ %U6211 %U662FCM ซึ่งการเข้ารหัสที่สอดคล้องกันของ "I" คือ 6211 การเข้ารหัสของ "ใช่" คือ 662F และการเข้ารหัสของ "CM" คือ CM
โปรดทราบว่า Escape () ไม่ได้ถูกเข้ารหัสโดย "+" แต่เรารู้ว่าเมื่อหน้าเว็บส่งแบบฟอร์มหากมีช่องว่างมันจะถูกแปลงเป็นอักขระ + เมื่อเซิร์ฟเวอร์ประมวลผลข้อมูลเครื่องหมาย + จะถูกประมวลผลลงในช่องว่าง ดังนั้นระวังเมื่อใช้
encodeuri
การเข้ารหัส URL ทั้งหมดจะใช้รูปแบบ UTF-8 เพื่อส่งออกสตริงที่เข้ารหัส อย่างไรก็ตาม ENCODEURI จะไม่เข้ารหัสอักขระพิเศษบางตัวยกเว้นการเข้ารหัส ASCII เช่น:! - - -
encodeuricomponent
แปลงสตริง URI เป็นสตริงรูปแบบหลบหนีในรูปแบบการเข้ารหัส UTF-8 เมื่อเทียบกับ encodeuri, encodeuricomponent จะมีประสิทธิภาพมากขึ้นและจะถูกเข้ารหัสสำหรับสัญลักษณ์ (; /?: @ & = + $, #) ที่ไม่ได้เข้ารหัสใน encodeuri () อย่างไรก็ตาม encodeuricomponent จะเข้ารหัสส่วนประกอบของ URL เป็นรายบุคคลเท่านั้นและจะไม่ถูกใช้เพื่อเข้ารหัส URL ทั้งหมด วิธีการถอดรหัสฟังก์ชั่นการถอดรหัสที่สอดคล้องกัน
แน่นอนเรามักจะใช้ปาร์ตี้ ENCODEURI เพื่อดำเนินการเข้ารหัส การเข้ารหัส JavaScript ที่เรียกว่าและถอดรหัสสองครั้งในพื้นหลังคือการใช้วิธีนี้ มีวิธีแก้ปัญหาสองวิธีในการแก้ปัญหานี้ใน JavaScript: การแปลงรหัสหนึ่งครั้งและวิธีการแปลงรหัสสองวิธี
การแปลงรหัสครั้งเดียว
JavaScript Transcoding:
var url = '<s: ค่าคุณสมบัติ = "WebPath" />/showMobLieqrCode.servlet?name=i Am CM'; window.location.href = encodeuri (url);
URL ที่ถูกแปลงรหัส: http://127.0.0.1:8080/perbank/showmobleqrcode.servlet?name=%E6%88%91%E6%98%AFCM
การประมวลผลแบ็กเอนด์:
ชื่อสตริง = request.getParameter ("ชื่อ"); System.out.println ("พารามิเตอร์ที่เข้ามาเบื้องหน้า:" + ชื่อ); name = new String (name.getBytes ("ISO-8859-1"), "UTF-8"); System.out.println ("พารามิเตอร์ที่ถอดรหัส:" + ชื่อ); ผลลัพธ์ผลลัพธ์:
พารามิเตอร์ที่เข้ามาในแผนกต้อนรับ: ???????
หลังจากถอดรหัสพารามิเตอร์: ฉันเป็น CM
การแปลงรหัสทุติยภูมิ
จาวาสคริปต์
var url = '<s: ค่าคุณสมบัติ = "WebPath" />/showMobLieqrCode.servlet?name=i Am CM'; window.location.href = encodeuri (encodeuri (url));
URL ที่ถูกแปลงรหัส: http://127.0.0.1:8080/perbank/showmobleqrcode.servlet?name=%25E6%2588%2591%25E6%2598%25AFCM
การประมวลผลแบ็กเอนด์:
ชื่อสตริง = request.getParameter ("ชื่อ"); System.out.println ("พารามิเตอร์ที่เข้ามาเบื้องหน้า:" + ชื่อ); name = urldecoder.decode (ชื่อ, "UTF-8"); System.out.println ("พารามิเตอร์ที่ถอดรหัส:" + ชื่อ); ผลลัพธ์ผลลัพธ์:
พารามิเตอร์ขาเข้าด้านหน้า: E68891E698AFCM
หลังจากถอดรหัสพารามิเตอร์: ฉันเป็น CM
กรอง
การใช้ตัวกรองตัวกรองมีสองประเภทแรกคือการตั้งค่าการเข้ารหัสและที่สองคือการดำเนินการถอดรหัสโดยตรงในตัวกรอง
ตัวกรอง 1
ตัวกรองนี้ตั้งค่ารูปแบบการเข้ารหัสของคำขอโดยตรง
ตัวละครระดับสาธารณะใช้ตัวกรอง {private filterConfig config; การเข้ารหัสสตริง = null; โมฆะสาธารณะทำลาย () {config = null; } โมฆะสาธารณะ dofilter (คำขอ ServletRequest, การตอบสนอง servletResponse, ห่วงโซ่ FilterChain) พ่น IOException, ServleTexception {request.Setcharacterencoding (การเข้ารหัส); chain.dofilter (คำขอ, การตอบกลับ); } public void init (filterConfig config) พ่น servletexception {this.config = config; // รับพารามิเตอร์การกำหนดค่าสตริง str = config.getInitParameter ("การเข้ารหัส"); if (str! = null) {encoding = str; - การกำหนดค่า:
<!-การกำหนดค่าตัวกรองภาษาจีน-> <filter> <silter-name> chinueencoding </filter-name> <silter-class> com.test.filter.characterencoding </filter--class> <init-Param> <param-name> <Stilter-Name> การเข้ารหัสภาษาจีน </filter-Name> <URL-PATTANTN>/*</URL-PATTENN> </SHILTER-MAPPANCE>
ตัวกรอง 2
ในวิธีการประมวลผลตัวกรองจะถอดรหัสพารามิเตอร์โดยตรงจากนั้นรีเซ็ตพารามิเตอร์ที่ถอดรหัสไปยังแอตทริบิวต์คำขอ
ตัวละครระดับสาธารณะใช้ตัวกรอง {ตัวกรองที่ได้รับการป้องกัน FilterConfig; การเข้ารหัสสตริง = null; โมฆะสาธารณะทำลาย () {this.filterConfig = null; } / *** เริ่มต้น* / public void init (filterConfig filterConfig) {this.filterConfig = filterConfig; } / *** แปลง INTR ลงในแบบฟอร์มการเข้ารหัสของ UTF -8** @Param Instr ป้อนสตริง* @return UTF - สตริงการเข้ารหัส 8 ของ 8* @throws unsupportencodeNingException* / สตริงส่วนตัว toutf (string instr) โยน unsupportencodexception {string outstr = ""; if (instr! = null) {outstr = new String (strop.getBytes ("ISO-8859-1"), "UTF-8"); } return outstr; } / *** การประมวลผลการกรองภาษาจีนที่อ่านไม่ออก* / โมฆะสาธารณะ dofilter (servletrequest servletrequest, servletresponse servletresponse, chain filterchain) โยน ioexception, servletexception httpservletResponse response = (httpservletResponse) servletResponse; // วิธีการรับคำขอ (1.post หรือ 2.get) และการประมวลผลที่แตกต่างกันจะดำเนินการตามวิธีการร้องขอวิธีการสตริง = request.getMethod (); // 1. สำหรับคำขอที่ส่งในโพสต์ตั้งค่าการเข้ารหัสโดยตรงเป็น UTF-8 ถ้า (method.equalsignorecase ("post")) {ลอง {request.setcharacterencoding ("UTF-8"); } catch (unsupportencodingexception e) {e.printstacktrace (); }} // 2. คำขอที่ส่งใน Get Else {// ออกชุดพารามิเตอร์ที่ส่งโดยการแจงนับไคลเอนต์ <String> paramNames = request.getParameterNames (); // traverse ตั้งค่าพารามิเตอร์เพื่อออกชื่อและค่าของแต่ละพารามิเตอร์ในขณะที่ (paramnames.hasmoreElements ()) {ชื่อสตริง = paramNames.nextElement (); // รับค่าสตริงชื่อพารามิเตอร์ [] = request.getParameterValues (ชื่อ); // นำค่าออกไปตามชื่อพารามิเตอร์ // ถ้าชุดค่าพารามิเตอร์ไม่ว่างเปล่าถ้า (ค่า! = null) {// traverse ชุดค่าพารามิเตอร์สำหรับ (int i = 0; i <ค่าความยาว; i ++) {ลอง {// วงกลมกลับ ค่า [i] = vlustr; } catch (unsupportencodingexception e) {e.printstacktrace (); }} // ซ่อนค่าในรูปแบบของแอตทริบิวต์ในคำขอคำขอคำขอ SetAttribute (ชื่อ, ค่า); }}} // ตั้งค่าวิธีการตอบกลับและสนับสนุนชุดอักขระจีนตอบสนองการตอบสนอง SetContentType ("ข้อความ/html; charset = utf-8"); // ดำเนินการต่อเพื่อเรียกใช้ตัวกรองถัดไป หากไม่มีตัวกรองคำขอจะเป็น chain.dofilter (คำขอ, การตอบกลับ); - การกำหนดค่า:
<!-การกำหนดค่าตัวกรองภาษาจีน-> <filter> <silter-name> การเรียนรู้ภาษาจีน </filter-name> <silter-class> com.test.filter.Characterencoding </filter-class> </filter>
อื่น
1. SET PAGEENCODING และ ContentType
<%@ page language = "java" contentType = "ข้อความ/html; charset = utf-8" pageencoding = "utf-8"%>%>
2. ตั้งค่า uriencoding ของ tomcat
โดยค่าเริ่มต้นเซิร์ฟเวอร์ TOMCAT ใช้รูปแบบการเข้ารหัส ISO-8859-1 เพื่อเข้ารหัส URL ที่ร้องขอโดยพารามิเตอร์ URIENCODING ดังนั้นเราจึงต้องเพิ่ม uriencoding = "UTF-8" ลงในแท็ก <concontor> ของไฟล์ Server.xml ของ TomCat