LDAP101 with ApacheDS
LDAP คืออะไร และลงมือทำจริงด้วย ApacheDS
สวัสดีครับ บทความนี้เราจะมาพูดถึง LDAP และทดลองสร้าง LDAP Server ด้วย ApacheDS กันครับ ในบทความจะอธิบายความรู้พื้นฐานที่จำเป็นเมื่อจะต้องทำงานกับ LDAP เช่น DIT, CN, DN, RDN เป็นต้น และมีตัวอย่างการใช้งานจริงมาให้ดูด้วย
มาพูดถึงเหตุผลที่ต้องมี LDAP กันก่อน เวลาที่เราสร้างระบบอะไรสักอย่างขึ้นมาใหม่ เราก็มักจะต้องสร้าง database สำหรับระบบนั้นๆ ขึ้นมาด้วย ซึ่งพอนานวันไป เราก็สร้างระบบใหม่เรื่อยๆ หรืออาจจะซื้อระบบจากข้างนอกมาใช้เพิ่มเรื่อยๆ เราก็ต้องเริ่ม maintain database หลายตัวมากขึ้น ซึ่งใน database เหล่านี้ ก็จะมีทั้งข้อมูลที่เฉพาะเจาะจงกับระบบนั้นๆ และข้อมูลบางอย่างที่เหมือนๆ กันในทุกระบบ เช่น ข้อมูลของ user ซึ่งประกอบไปด้วย username, full name, email, password (ที่ส่วนใหญ่ก็อยากใช้ password เดียวกันจะได้จำได้ง่ายๆ) นอกจาก database ของเราเองแล้ว ก็ยังรวมถึงเครื่องมือต่างๆ จาก 3rd party อีกด้วย เช่น Slack, MS Teams, GitLab เป็นต้น
ความยุ่งยากของการมีข้อมูลเหมือนๆ กันหลายๆ ที่คือมัน maintain ลำบาก ตัวอย่างเช่น ถ้าเราอยากจะเปลี่ยน password ก็ต้องไปตามเปลี่ยนให้ครบทุกที่ เวลามีพนักงานเข้ามาใหม่หรือพนักงานเดิมลาออกทีก็ต้องไปเพิ่มลบ account ในทุกระบบเลย ฟังดูลำบากมากๆ
ถึงจุดหนึ่งเราก็จะเกิดความคิดว่า อยากจะแยกระบบ account ของ service และ tool เหล่านี้ออกมา ให้ทุกคนมาใช้ระบบ authentication/authorization กลางให้หมด จะได้แก้ปัญหาเรื่องที่ต้อง maintain หลายๆ ที่ ซึ่งเจ้า LDAP นี่เองที่มาตอบโจทย์ตรงนี้ มันเป็นเครื่องมือที่ช่วยเก็บข้อมูลคล้ายๆ database แต่รองรับโดย software และ tool ดังๆ เยอะมากๆ ทำให้เราสามารถเชื่อม tool ข้างนอกเข้ามาได้โดยไม่ต้องเขียน code เพิ่มเลย
Authentication (การยืนยันตัวตน) คือการเช็คว่า user คนนี้ใช่ user คนนี้จริงๆ หรือเปล่า เช่น ถ้าใส่ username กับ password มาถูก หรือ แสกนนิ้วมือแล้วตรง ก็ถือว่าใช่ user คนนี้จริงๆ
Authorization (การตรวจสอบสิทธิ์) คือการเช็คว่า user คนนี้มีสิทธิ์ทำสิ่งนี้จริงหรือเปล่า เช่น user A อาจจะยืนยันตัวตนแล้วว่าเป็น user A จริง แต่ user A ก็อาจจะไม่มีสิทธิ์ลบไฟล์บางไฟล์ เป็นต้น
LDAP คืออะไร
LDAP หรือ Lightweight Directory Access Protocol แยกอธิบายตามชื่อเลยคือ
- Lightweight แปลง่ายๆ ว่า เป็นรุ่นที่ simple กว่า ในที่นี้คือเทียบกับ protocol ตัวก่อนหน้าอย่าง X.500 ที่มี feature ที่ไม่ค่อยได้ใช้งานค่อนข้างเยอะ และ config ยากกว่ามาก
- Directory Access ให้จินตนาการว่ามันทำหน้าที่คล้ายๆ file system ที่เราใช้ๆ กันอยู่เลย เช่น มีไฟล์ มี folder ข้างในไฟล์ก็มีข้อมูลต่างๆ เราสามารถลบ เพิ่ม หรือแก้ไขไฟล์ได้
- Protocol แปลว่า LDAP เป็นแค่ Protocol หรือเป็นแค่วิธีการสื่อสารระหว่าง host เช่น client กับ server เท่านั้น ไม่ได้เป็นระบบที่พร้อมทำงานได้ ซึ่งระบบที่เรารู้จักกันดีอย่าง OpenLDAP, ApacheDS หรือ Microsoft Active Directory ก็คือคนที่นำเอา protocol ไป implement (ส่วนใหญ่นอกจาก implement protocol LDAP แล้วก็ยังใส่ feature อีกมากมายเข้าไปด้วย)
สรุปแล้ว LDAP เป็นเอกสารที่กำหนดวิธีการสื่อสารกันระหว่าง client กับ sever เพื่อที่จะให้บริการ Directory Service เช่น ค้นหาไฟล์ ขอดูข้อมูลในไฟล์ ขอลบไฟล์ ขอเพิ่มไฟล์เป็นต้น และ software ที่ implement protocol LDAP เราจะเรียกรวมๆ ว่า LDAP Server
สิ่งที่ LDAP แตกต่างจาก file system ทั่วไป หรือ database คือ
- LDAP ถูกออกแบบมาโดยมีสมมติฐานว่าการอ่านข้อมูลจะต้องมีมากกว่าการเขียนมากๆ ตัวอย่างเช่น ระบบเก็บข้อมูล user ส่วนใหญ่จะเน้นอ่านข้อมูล user มากกว่า นานๆ ทีจะเข้ามาแก้ไขข้อมูล
- LDAP ไม่ได้ถูกออกแบบมาให้เก็บข้อมูล blob หรือพูดง่ายๆ ก็คือมันถูกออกแบบมาให้เก็บข้อมูลพวก text มากกว่าข้อมูลที่เป็น binary
มาเริ่มกันเลย
ลองมาดูตัวอย่าง model วิธีการเก็บข้อมูลของ LDAP กันก่อน
จากรูปจะเห็นว่า LDAP ใช้วิธีการเก็บข้อมูลแบบ tree เหมือนที่เราคุ้นเคยใน file system ทั่วไปเลยครับ ก่อนที่เราจะไปพูดถึงรายละเอียดของ tree จะมี term ที่ควรรู้จักคร่าวๆ ก่อนคือ
- DIT (Data Information Tree) คือข้อมูลทั้งหมดใน 1 Tree
- Entry ใน LDAP จะเรียกทุก node ใน tree ว่าเป็น Entry ไม่ได้แบ่งแยกเป็น file และ folder เหมือนที่เราคุ้นเคย
- objectClass: ภายในแต่ละ Entry จะมีข้อมูลในรูปแบบ key-value อยู่ (ใน LDAP จะเรียกว่า attribute กับ value ของ attribute) ซึ่งคนที่เป็นคนกำหนดว่าจะมีชื่อ attribute อะไรได้บ้าง เช่น cn, sn, telephoneNumber ในตัวอย่าง ก็คือ objectClass ใครเขียน Java ได้ ให้จินตนาการว่า objectClass เหมือนกับ Parent Class ในภาษา java เลยครับ Entry 1 ตัวสามารถเป็นลูกของหลายๆ objectClass ได้
- DN: Distinguished Name เป็นชื่อของแต่ละ Entry ที่จะไม่มีโอกาสซ้ำกันได้เลย เหมือนกับ absolute path ของ file system
- RDN: Relative Distinguished Name เป็นชื่อเรียก Entry ที่จะไม่ซ้ำกันใต้ parent เดียวกัน ก็คือเหมือนกัน relative path ของ file system
- นอกจากนั้นก็จะเป็นชื่อ Attribute ตามที่แต่ละ objectClass ออกแบบไว้ครับ เช่น cn (Common Name), sn (Surname), dc(Domain Component) ถ้าเห็นแต่ชื่อย่อก็จะงงๆ ครับ แต่ถ้าเห็นชื่อเต็มมันแล้วก็จะไม่ค่อยงงเท่าไหร่ เราสามารถค้นหาโครงสร้างและชื่อเต็มของแต่ละ objectClass และ attribute ได้ที่ https://ldapwiki.com/wiki/ObjectClass
เสริมตรง objectClass นิดนึงครับ อย่างที่บอกว่า objectClass แต่ละตัวจะกำหนด attribute ที่ตัวมันรองรับได้ ซึ่ง attribute เหล่านี้จะแบ่งออกเป็น 2 กลุ่มหลักๆ คือกลุ่ม MUST และ Optional กลุ่ม MUST คือ Entry ไหนที่มาใช้ objectClass นี้จะต้องมี attribute นี้ด้วยเสมอ กลุ่ม Optional ก็คือ Entry ที่มาใช้จะมีหรือไม่มี attribute นี้ก็ได้
ลองมาดูตัวอย่างจากรูปด้านบน
- Entry สีแดงจะมี DN และ RDN เหมือนกันคือ dc=insightera,dc=co,dc=th
- Entry สีเขียวจะมี DN เป็น ou=devices,dc=insightera,dc=co,dc=th และมี RDN เป็น ou=devices ถ้าสังเกตดีๆ จะเห็นว่า DN ก็คือการเอา RDN ของ Entry ตั้งแต่ root จนมาถึงตัวมันเองมาต่อๆ กันนั่นเอง
- Entry สีฟ้าก็คล้ายๆ กับสีเขียวเลย คือ RDN เป็น ou=people และ มี DN เป็น ou=people,dc=insightera,dc=co,dc=th
- Entry สีส้มจะมี RDN เป็น cn=punyapat.s และมี DN เป็น cn=punyapat.s,ou=people,dc=insightera,dc=co,dc=th จะเห็นว่า RDN ไม่จำเป็นต้องเป็น attribute ทุกตัวของ Entry แต่ขอให้ unique ใต้ parent ตัวเดียวกันก็พอครับ ใน Entry สีส้มก็จะมี Attribute อื่นๆ อีกเช่น sn=sessomboon (นามสกุล) หรือ telephoneNumber = 089–999–9999 ด้วย
ความจริงแล้วต่อจากนี้เราจะต้องพูดถึง LDIF (LDAP Interchange Format) กันอย่างละเอียดครับ ซึ่ง LDIF เป็น text ไฟล์ที่ใช้เพื่อบอกว่าจะให้ เพิ่ม ลบ แก้ไข Entry อย่างไรบ้าง แต่เนื่องจากเราจะใช้ ApacheDS เป็น LDAP Sever และ ApacheDS จะมาพร้อมกับ Apache Directory Studio ซึ่งเป็น GUI tool ผมจะขอพูดถึง LDIF แค่คร่าวๆ แทนครับ
ตัวอย่างไฟล์ LDIF เพื่อเพิ่ม Entry ใหม่
หลังจากนั้นก็ใช้คำสั่ง ldapadd
ldapadd -f example_add.ldif
ถึงตรงนี้ไม่ต้องตกใจถ้า run ตามไม่ได้นะครับ อันนี้เป็นแค่ตัวอย่างการใช้ command line กับไฟล์ แต่ต่อจากนี้เราจะไปใช้ GUI กันครับ
ApacheDS
อย่างที่เกริ่นไปก่อนหน้านี้ว่า LDAP เป็นแค่ protocol และคนที่เอา protocol นี้ไป implement เราจะเรียกรวมๆ ว่า LDAP Server ซึ่ง ApacheDS เองก็เป็นระบบหนึ่งที่ implement LDAPv3 และมี feature อื่นๆ นอกเหนือจากใน LDAP อีกมากมาย นอกจาก ApacheDS ก็มีตัวเลือกอื่นๆ อีกนะครับ เช่น OpenLDAP, Microsoft Active Directory ตอนแรกผมกะว่าจะเลือก OpenLDAP มาเขียนบทความ แต่ทดลองติดตั้งโปรแกรมเองแล้ว หมดไปหลายชั่วโมงเลย ต้อง compile โปรแกรมเอง ต้องจัดหา dependency มากมายมาติดตั้งก่อน ก็เลยคิดว่าไม่น่าจะเหมาะกับ บทความ 101 ของเรา เลยเลือก ApacheDS เพราะแทบจะคลิกเดียวเสร็จ
ติดตั้ง ApacheDS
ผมใช้ ubuntu18.04 นะครับ ถ้าใช้ linux ตัวอื่น หรือ Windows, OSX ลองดูวิธีติดตั้งได้ที่ http://directory.apache.org/apacheds/basic-ug/1.3-installing-and-starting.html
- ติดตั้ง Java (ผมไม่อธิบายในบทความนะครับ ใน Google มีเพียบเลย)
- Download file ติดตั้งที่ http://directory.apache.org/apacheds/download/download-linux-bin.html จะได้ .bin มา
- ติดตั้งโดย sudo ./apacheds-2.0.0.AM25–64bit.bin หลังจากนั้นก็ ตอบ yes กด next ตามค่า default ไปเลยครับ
- ลองสั่ง sudo service apacheds-2.0.0.AM25-default status (เลข version อาจจะไม่เหมือนกัน) จะเห็นว่าโปรแกรมยังไม่ active
- สั่งให้โปรแกรมเริ่มทำงานด้วย sudo service apacheds-2.0.0.AM25-default start แล้วลองเช็ค status อีกทีจะเห็นว่าโปรแกรมเริ่มทำงานแล้ว
*ถ้า start ไม่สำเร็จลอง start ใน mode console จะเห็น log มากขึ้น
sudo service apacheds-2.0.0.AM25-default console
สำหรับใครที่ใช้งาน Docker เป็น สามารถติดตั้ง ApacheDS ง่ายๆ ด้วย image นี้ได้เลยครับ https://hub.docker.com/r/openmicroscopy/apacheds ถ้าใช้คำสั่งตามใน link ที่ให้ไป ApacheDS จะ listen ที่ port 389 ก็ปรับ config ของ Studio (ที่กำลังจะอธิบายด้านล่าง) ตามนะครับ และอย่าลืม bind data directory ออกมาด้วยนะ ตัวอย่างเช่น
docker run -d -p 389:10389 -v /home/perth/apacheds_data/:/var/lib/apacheds openmicroscopy/apacheds
ติดตั้ง Apache Directory Studio
- Download file ติดตั้งที่ http://directory.apache.org/studio/download/download-linux.html จะได้ .tar.gz มา
- แตกไฟล์ด้วย tar xf ApacheDirectoryStudio-2.0.0.v20180908-M14-linux.gtk.x86_64.tar.gz
- เข้าไปใน folder ที่เพิ่งแตกออกมา cd ApacheDirectoryStudio
- เริ่มต้น program ด้วยคำสั่ง ./ApacheDirectoryStudio (เดี๋ยวค่อยไปหาวิธีติดตั้งให้เรียกใช้ง่ายๆ กันทีหลังนะครับ)
เราจะได้ program หน้าตาแบบนี้
สร้าง connection ไปที่ LDAP Server (ApacheDS)
คลิกขวาที่มุมซ้ายล่าง ตรงหน้าต่าง connections เลือก New connection…
ใส่ hostname เป็น localhost และ port เป็น 10389 (default port ของ LDAP คือ 389 ซึ่งเราสามารถไปเปลี่ยนทีหลังได้ครับ) กด Next
หน้าต่อไปจะต้องใส่ DN ของ admin เพื่อ Authen และเริ่มใช้งาน LDAP โดยใน LDAP จะเรียกการ Authentication ว่า Binding ใส่ DN เป็น uid=admin,ou=system และ default password คือ secret (เดี๋ยวหลังจากนี้เราจะไปเปลี่ยน admin password กัน) เสร็จแล้วคลิก Finish
เราจะได้ connection ใหม่ และได้ partition เริ่มต้นมา 1 อัน (Partition ใน ApacheDS หมายถึง DIT 1 ต้น) ชื่อว่า dc=example,dc=com (สังเกตไหมครับ เราจะอ้างถึงชื่อของ Entry ด้วย DN หรือ RDN เสมอ) โดยมี objectClass ของ root entry เป็นประเภท domain (example.com) ครับ และ dc ก็หมายถึง Domain Component ก็คือการแยกด้วย . เช่น insightera.co.th ก็จะเป็น dc=insightera,dc=co,dc=th เป็นต้น
ทดลองสร้าง Entry ใหม่โดยคลิกขวาที่ชื่อ dc=example,dc=com แล้วเลือก New Entry… แล้วเลือก create entry from scratch
หน้าต่อไปคือหน้าที่ต้องเลือก objectClass ของ Entry ที่เรากำลังจะสร้าง โดยให้ทดลองเลือกเป็น organizationalUnit จะเห็นว่า program จะเลือก objectClass ให้เราอัตโนมัติ 2 ตัวคือorganizationalUnit เอง และ Top ถ้าสังเกตดีๆ จะเห็นตัวอักษร A และ S ที่ icon ของ objectClass ตัวอักษรจะช่วยบอกว่า objectClass นี้เป็นประเภทอะไร โดย objectClass ใน LDAP จะมีอยู่ 3 ประเภท คือ
- Structural object class ก็คือ objectClass ส่วนใหญ่ที่เราจะใช้
- Auxiliary object class เป็น objectClass ที่ทำหน้าที่กำหนดคุณสมบัติบางอย่างให้ objectClass อื่น ตัวมันเองไม่สามารถถูกนำไปใช้ตรงๆ ได้
- Abstract object class ก็เหมือนกับ Abstract class ในภาษา Java คือไม่สามารถถูกนำไปใช้ตรงๆ ได้ แต่เป็น parent ของ class อื่นได้ ตัว Top ที่เราเห็นก็เป็น Class ประเภทนี้ครับ โดย Top เป็น objectClass พิเศษที่เป็น parent ของ objectClass ที่เหลือทุกตัว
หน้าต่อไป เป็นหน้าสำหรับกำหนด Attribute ต่างๆ ของ Entry ที่เรากำลังจะสร้าง โดยทดลองใส่ ou=people แล้วกด next และ finish (ou เป็น attribute หนึ่งของ objectClass oranizationalUnit)
เราจะได้ ou มาใหม่ 1 ตัว
เป็นยังไงครับ ง่ายกว่าเขียนไฟล์ LDIF เองมากๆ เพราะมีระบบคอย suggest ค่าต่างๆ ที่ต้องใส่ให้ พร้อมทั้งตรวจสอบความถูกต้องให้ด้วย การลบและแก้ไขก็สามารถทำได้ผ่าน GUI เลย
เปลี่ยน admin password
ก่อนจะไปหัวข้อถัดไป อย่าลืมเปลี่ยน password admin กันก่อนครับ เพราะตอนนี้เราใช้ค่า default อันตรายมากที่คนอื่นจะเข้ามาแก้ไข LDAP Server ของเรา
การเปลี่ยน password ก็ทำไม่ได้ยากเลยครับ เรารู้อยู่แล้วว่า default DN คือ uid=admin,ou=system เราไปดูว่า Entry นี้มี attribute อะไรบ้าง
แก้ค่า attribute userPassword โดย double click ที่ attribute แล้วเลือก New password แล้วก็แค่กรอก password ใหม่เลย เลือก hash เป็น SHA1 ไว้ด้วยครับ เผื่อใครแอบมา hack เอา database ข้างหลังเราไปได้ เขาก็จะได้ไปแค่ hash
เสร็จแล้วก็ตามไปแก้ setting ของ connection ที่เราใช้ default password ไว้ด้วยนะครับ ไม่งั้นต่อจากนี้จะทำอะไรไม่ได้เลย
ปิด Anonymous Access
ApacheDS จะเปิด anonymous access เป็น default เราต้องเข้าไปปิดมันก่อน ไม่อย่างนั้นใครก็จะสามารถเชื่อมต่อเข้ามาเพื่อดูข้อมูลในระบบเราได้
คลิกขวาที่ connection แล้วเลือก Open configuration… แล้วเอาเครื่องหมายถูกออกในช่องที่เขียนว่า Allow Anonymous Access ออก แล้วก็ ctrl+s (save)
Stop ApacheDS แล้ว start ใหม่ ด้วยคำสั่ง
sudo service apacheds-2.0.0.AM25-default restart
ลอง Authentication ด้วย library python
ต่อไปเราลองมาทำ authen ง่ายๆ ด้วย lib ของภาษา python กันครับ ก่อนที่เราจะไปเขียน code python เราต้องสร้าง user คนใหม่ขึ้นมาก่อน (เพราะเราไม่ควรเอา admin user ไปใช้ตรงๆ)
วิธีการสร้าง user ก็เหมือนกับการสร้าง Entry ทั่วไป โดยเราจะสร้างไว้ใน ou=people ที่เราเพิ่งสร้างขึ้นมา ใช้วิธีการเดียวกันกับตอนที่สร้าง ou=people แต่เปลี่ยนจากเลือก organizationalUnit เป็นเลือก Person แทน
เหตุผลที่เราต้องเลือก person เพราะว่า objectClass นี้ถูกออกแบบมาให้เก็บ password ของ user ได้และรองรับการทำ authentication
เลือก cn=punyapat.s (หรืออะไรก็ได้ตามใจผู้อ่าน) แล้วกด Next จะเห็นว่าหน้าต่างที่ให้แก้ไขค่า attribute มี sn เพิ่มมาให้อัตโนมัติ สาเหตุก็เพราะว่า sn เป็น attribute ที่เป็น MUST คือ Entry ที่จะใช้ objectClass person ต้องมี attribute นี้ด้วยเสมอ เราก็ใส่ค่า sn (Surname) ไปเลยครับ
เสร็จแล้วเราก็จะได้ Entry ใหม่มา 1 ตัว ต่อจากนี้เราก็จะมาตั้ง password ให้กับ user ของเรากัน (username คือ DN) โดยคลิกขวาที่พื้นที่ว่างเปล่าของหน้าต่าง attribute แล้วเลือก New attribute… เลือก attribute type เป็น userPassword แล้วกด Finish
จะเห็นหน้าต่างคล้ายๆ กับตอนที่ใช้เปลี่ยน password ของ admin เราก็ใช้วิธีเดียวกันตั้ง password ให้ user คนนี้เลย
เขียน program python เพื่อทำ authentication
ขั้นตอนการ authen ก็ตรงไปตรงมาครับ เริ่มจากเชื่อมต่อกับ server ก่อน แล้วก็ใส่ username (DN) กับ password (12345) ถ้า authen ไม่สำเร็จ program ก็จะแสดง error แล้วปิดตัวไป ถ้า authen สำเร็จ program จะทำงานต่อแล้วไปใช้คำสั่ง search ต่อไป (ใน LDAP bind = authenticate)
คำสั่ง search นี้เป็นตัวอย่างอย่างง่ายของการ search โดย
- ou=people,dc=example,dc=com เป็น base ของการ search แปลว่าเราจะค้นหาเฉพาะสิ่งที่อยู่ใต้ Entry นี้
- ldap.SCOPE_SUBTREE แปลว่าให้ค้นลงไปตาม sub-tree เรื่อยๆ scope การค้นจะมีหลายแบบเช่น ค้นหาเฉพาะใน Entry นี้ หรือค้นหาเฉพาะลูกของ Entry นี้เป็นต้น แต่ส่วนใหญ่ก็จะเป็น scope แบบ sub-tree
- [‘cn’] คือ attribute ที่เราสนใจ ถ้าเราไม่กำหนดข้อนี้ LDAP จะคืนข้อมูลทุกอย่างใน Entry มาให้ ซึ่งอาจจะทำให้เราได้ข้อมูลที่ไม่ต้องการมาเพิ่มและเปลือง bandwidth
ผลลัพธ์
[('cn=punyapat.s,ou=people,dc=example,dc=com', {'cn': [b'punyapat.s']}), ('ou=people,dc=example,dc=com', {})]
- ** Note ตรงนี้ไว้นิดนึงนะครับว่าอันนี้ไม่ใช่วิธีปกติที่ใช้สำหรับ Authen ด้วย LDAP ครับ แต่เป็นวิธีหนึ่งที่ใช้งานได้ไม่มีปัญหาอะไร แต่ถ้าอยากรู้ว่าวิธีมาตรฐานเขาทำกันแบบไหน แนะนำให้อ่านต่อในบทความข้างล่างครับ
TLDR: เราจะต้องสร้าง user กลางขึ้นมาตัวนึงเพื่อใช้ทำ binding ครับ
ประมาณนี้ครับ วิธีใช้งาน LDAP บน ApacheDS คร่าวๆ ยังเหลือหัวข้อที่น่าสนใจอีกเยอะมาก เช่น authorization และ advance search เป็นต้น และยังมี feature อื่นๆ ของ ApacheDS ที่เรายังไม่ได้พูดถึงอีก อ่านได้ที่บทความต่อๆ ไปเลยครับ
ใครที่สนใจอ่านเองต่อ สามารถอ่านได้จากแหล่งอ้างอิงตามนี้ครับ