จริงๆ แล้วระบบธนาคารที่สร้างเองกำลังทำงานอะไรอยู่ — สำรวจโครงสร้าง หน้าจอ และโค้ดหลักทั้งหมด

Java Spring Boot 金融 アーキテクチャ 認証 API

บทนำ

ฉันกำลังสร้างระบบธนาคารด้วยตัวเองคนเดียว จนถึงขั้นที่ใช้งานได้จริง ไม่ใช่แค่ออกแบบเท่านั้น ลำดับทั้งหมด—มีบัญชี ดูยอดคงเหลือ ฝากถอน โอนเงิน—ทำงานต่อเนื่องกันตั้งแต่หน้าจอ ไปจนถึง API และฐานข้อมูล กระบวนการตั้งแต่การกำหนดความต้องการจนถึงการออกแบบ สรุปไว้ใน การสร้าง API ธนาคารที่เริ่มต้นจากการออกแบบ

บทความนี้เป็นภาคต่อ จะพาสำรวจ “จริงๆ แล้วอะไรกำลังทำงานอยู่” ผ่านโครงสร้าง หน้าจอ และโค้ดหลัก บทความเรื่องความปลอดภัยและการอิมพลีเมนต์ที่จะเขียนต่อจากนี้ ถือว่าการสำรวจนี้เป็นพื้นฐานร่วมกัน เพื่อไม่ต้องอธิบายรายละเอียดซ้ำทุกครั้ง จึงวางฐานร่วมนี้ไว้ที่นี่


โครงสร้างโดยรวม

เบื้องหลังหน้าจอของผู้ใช้ มีหลายเซอร์วิสแบ่งหน้าที่กันทำงาน หน้าจอไม่แตะฐานข้อมูลโดยตรง แต่มีชั้นตรงกลาง (BFF) ทำหน้าที่จบการยืนยันตัวตน และรวบรวมคำขอไปยังแต่ละเซอร์วิส

   ผู้ใช้ (เบราว์เซอร์ / มือถือ)


   ┌──────────────────────────────────────────┐
   │ BFF: รวบรวม API เบื้องหลังหน้าจอ และจบการยืนยันตัวตน │
   └──────────────────────────────────────────┘

          ├─▶ เซอร์วิสยืนยันตัวตน (ล็อกอิน ยืนยันตัวบุคคล)
          ├─▶ เซอร์วิสบัญชี (ยอดคงเหลือ ฝากถอน โอนเงิน)
          └─▶ เซอร์วิสแจ้งเตือน (แจ้งเตือนธุรกรรม)

               แต่ละเซอร์วิสมีฐานข้อมูลของตัวเอง

แทนที่จะเป็นระบบก้อนเดียว ฉันแยกการยืนยันตัวตน บัญชี และการแจ้งเตือนออกเป็นเซอร์วิสแยกกัน การแยกขอบเขตไว้ทำให้เรื่องของฝ่ายหนึ่งซึมเข้าไปอีกฝ่ายได้ยากขึ้น


หน้าจอ

ลูกค้าใช้งานผ่านเบราว์เซอร์ (React) การล็อกอินไม่ใช่รหัสผ่าน แต่เป็นพาสคีย์ (FIDO/WebAuthn)

หน้าจอล็อกอิน (พาสคีย์ / FIDO เป็นข้อมูลสาธิตของผลงานนี้)

เมื่อล็อกอินผ่าน หน้าโฮมจะแสดงบัญชีและยอดคงเหลือของตัวเองเรียงกัน เรื่องความปลอดภัยที่เขียนต่อจากบทความนี้ มีฉากอยู่ที่จุด “การดำเนินการกับบัญชีของตัวเอง” นี้พอดี

หน้าโฮม (บัญชีและยอดคงเหลือของตัวเอง เป็นข้อมูลสาธิตของผลงานนี้ ไม่ใช่ชื่อหรือยอดเงินจริง)

หน้าจอทำงานได้อย่างสวยงาม แต่สิ่งที่สำคัญคือสิ่งที่อยู่เบื้องหลัง ตรวจสอบว่าใครกำลังล็อกอินอยู่ และควบคุมให้คนคนนั้นแตะได้แค่บัญชีของตัวเอง—นี่คือแกนหลักของระบบนี้


การยืนยันตัวตนและ “ตัวบุคคล”

เมื่อล็อกอินผ่าน ระบบจะพาตัวบุคคลนั้นไปในฐานะ “ตัวบุคคล (principal)” ตั้งแต่นั้นมา API ทุกตัวจะมองตัวบุคคลนี้ในทุกคำขอ และตัดสินว่า “คนคนนี้ ทำสิ่งนี้ กับเป้าหมายนี้ ได้หรือไม่”

   ล็อกอิน ─▶ กำหนดตัวบุคคล (principal)

                     ├─▶ อ้างอิง (GET): คืนเฉพาะบัญชีของตัวบุคคลนั้น
                     └─▶ อัปเดต (POST): ให้ดำเนินการได้เฉพาะบัญชีของตัวบุคคลนั้น

                       (จุดนี้คือฉากของบทความเรื่องความปลอดภัยถัดไป)

สิ่งที่สำคัญตรงนี้คือ “ล็อกอินผ่าน” กับ “แตะบัญชีนี้ได้” เป็นการตัดสินคนละเรื่องกัน อย่างแรกคือการยืนยันตัวตน อย่างหลังคือการอนุญาตสิทธิ์ คนที่ล็อกอินแล้วไม่ได้แปลว่าจะแตะบัญชีของคนอื่นได้


โค้ดหลัก: การตรวจสอบความเป็นเจ้าของใน API ฝั่งอ้างอิง

ใน API ฝั่งอ้างอิง (GET) อย่างการดูยอดคงเหลือ กฎ “เฉพาะบัญชีของตัวบุคคลนั้น” นี้ถูกวางไว้ตั้งแต่ช่วงต้น ตรวจสอบว่าตัวบุคคลที่ได้จากการยืนยันตัวตน ตรงกับเจ้าของบัญชีเป้าหมายหรือไม่ และปฏิเสธหากไม่ตรงกัน

@GetMapping("/accounts/{id}/balance")
public BalanceResponse balance(@PathVariable String id,
                               @AuthenticationPrincipal Jwt principal) {

    String me = principal.getSubject();          // ตัวบุคคลที่กำหนดโดยการยืนยันตัวตน
    Account account = accounts.findById(id);

    // ตรวจสอบความเป็นเจ้าของ: บัญชีเป้าหมายเป็นของตัวบุคคลนั้นหรือไม่?
    if (!account.getCustomerId().equals(me)) {
        throw new AccessDeniedException("not your account");
    }
    return BalanceResponse.of(account);
}

สั้นแต่แกนหลักอัดแน่นอยู่ตรงนี้ principal คือตัวบุคคลที่กำหนดโดยการยืนยันตัวตน account.getCustomerId() คือเจ้าของบัญชี นำสองอย่างมาเทียบกัน หากไม่ตรงกันก็ไม่ให้แตะ ฝั่งอ้างอิงได้รับการป้องกันด้วยรูปแบบนี้

ปัญหาคือ “รูปแบบที่ป้องกันไว้แล้ว” นี้ ไม่ถูกส่งต่อไปยังฝั่งอัปเดต (ฝากถอน โอนเงิน) โดยอัตโนมัติ—เรื่องนี้จะเล่าต่อในบทความถัดไป


ตำแหน่งของบทความนี้

จนถึงตรงนี้คือการสำรวจ “ระบบนี้คืออะไร” ทั้งหมด—โครงสร้าง (แยกการยืนยันตัวตน บัญชี และการแจ้งเตือนออกจากกัน) หน้าจอ (ล็อกอิน การดำเนินการกับบัญชี) แกนหลัก (กำหนดตัวบุคคลและป้องกันด้วยการตรวจสอบความเป็นเจ้าของ) บทความถัดจากนี้จะสร้างบนพื้นฐานนี้ และจัดการกับหลุมพรางหรือการตัดสินใจในการออกแบบที่เจอระหว่างอิมพลีเมนต์ ทีละเรื่อง


บทความที่เกี่ยวข้อง

ส่งข้อความได้ตามสบาย

ไม่ว่าจะเป็นการว่าจ้างงาน แนะนำโปรเจกต์ ความคิดเห็น หรือคำถาม ยินดีรับทั้งหมด ฉันหวังเป็นอย่างยิ่งว่าจะได้เชื่อมต่อกับผู้ที่มีอุดมการณ์อันสูงส่งเช่นเดียวกัน ฉันจะมุ่งมั่นท้าทายในสิ่งที่ทุ่มเททั้งชีวิตต่อไป ขอขอบคุณและฝากเนื้อฝากตัวด้วยครับ/ค่ะ