ข้าม​ไป​ยัง​เนื้อหา

เอา body กลับ​เข้า​สัญญา แล้ว​พบ​ว่า​ต้อง​ลงมือ​สอง​ที่

บท​ที่ 1 วัด​ได้​ว่า​คำ​ตอบ​ที่​ปฏิเสธ​ผู้​เรียก​มี body ว่าง​เปล่า และ advice ของ​เรา​เอื้อม​ไม่​ถึง · สี่บท​ที่​ผ่าน​มา​ไม่มี​บท​ไหน​แก้​ข้อ​นั้น​เลย · บท​นี้​แก้

filter chain มี​สอง​ทางออก และ​แต่ละ​ทาง​มี​ตัว​เขียน​คำ​ตอบ​ของ​ตัว​เอง

หัวข้อ​ที่​มีชื่อ​ว่า “filter chain มี​สอง​ทางออก และ​แต่ละ​ทาง​มี​ตัว​เขียน​คำ​ตอบ​ของ​ตัว​เอง”

ถ้า advice อยู่​ผิด​ชั้น ก็​ต้อง​เขียน​ของ​ที่​อยู่​ใน​ชั้น​ที่​ถูก

filter chain มี​สอง​ทางออก​สำหรับ​คำขอ​ที่​ไม่​ผ่าน และ​แต่ละ​ทาง​มี​ตัว​เขียน​คำ​ตอบ​ของ​ตัว​เอง

  • AuthenticationEntryPointAuthenticationEntryPointตัว​ที่​เขียน​คำ​ตอบ​ให้​ผู้​เรียก​ที่​ยัง​ไม่​ได้​ยืนยัน​ตัว · เป็น​ที่​เดียว​ที่​เรา​เติม body ให้ 401 ได้ เพราะ advice เอื้อม​ไม่​ถึง · สำหรับ​ผู้​เรียก​ที่​ยัง​ไม่​ยืนยัน​ตัว
  • AccessDeniedHandlerAccessDeniedHandlerตัว​ที่​เขียน​คำ​ตอบ​ให้​ผู้​เรียก​ที่​ยืนยัน​ตัว​แล้ว​แต่​ไม่มี​สิทธิ์ · คู่​กับ​ตัว​บน​คือ​สอง​ที่​ที่​ต้อง​เขียน​เอง ถ้า​อยาก​ให้ body ของ security กลับ​เข้า​สัญญา​เดิม · สำหรับ​ผู้​เรียก​ที่​ยืนยัน​ตัว​แล้ว​แต่​สิทธิ์​ไม่​พอ

บท​ที่ 3 อธิบาย​ไว้​แล้ว​ว่า​ใคร​เป็น​คน​เลือก​ว่า​คำขอ​จะ​ไป​ทาง​ไหน · บท​นี้​เขียน​สิ่ง​ที่​รอ​อยู่​ปลาย​ทาง

รูป​เดียว​กัน​จริงๆ ไม่ใช่​คล้าย​กัน

ตัว​เขียน body ที่​ใช้​ร่วม​กัน​ทั้ง​สอง​ทาง
private fun write(response: HttpServletResponse, request: HttpServletRequest, code: Int,
title: String, writtenBy: String) {
response.status = code
response.contentType = "application/json"
response.writer.write(
"""{"type":"about:blank","title":"$title","status":$code,""" +
""""instance":"${request.requestURI}","kaenWrittenBy":"$writtenBy"}"""
)
}

kaenWrittenBy ไม่ใช่​ส่วน​หนึ่ง​ของ​สัญญา · มัน​คือ marker ที่​ใส่​ไว้​เพื่อ​พิสูจน์​ว่า ใคร​เป็น​คน​เขียน body ใบ​นั้น · บท​นี้​ใช้​มัน​ตอบ​คำถาม​ที่​รหัส​ตอบ​กลับ​ตอบ​ไม่​ได้

ติด​ตั้ง​ทั้ง​สอง​ตัว​เข้า​กับ chain
http.exceptionHandling {
it.authenticationEntryPoint(ContractEntryPoint())
it.accessDeniedHandler(ContractDeniedHandler())
}

สาม​ช่อง​ที่​เคย​ได้​ศูนย์​คีย์ ได้​ห้า​คีย์​เท่า​กัน​หมด

หัวข้อ​ที่​มีชื่อ​ว่า “สาม​ช่อง​ที่​เคย​ได้​ศูนย์​คีย์ ได้​ห้า​คีย์​เท่า​กัน​หมด”

สาม​ช่อง​ที่​เคย​ได้​ศูนย์​คีย์ ได้​ห้า​คีย์​เท่า​กัน​หมด

ช่องก่อนหลังใคร​เขียน
GET /boom ที่​โยน exception500 · 5 คีย์500 · 5 คีย์advice เหมือน​เดิม
GET /open ที่ permitAll ไว้200 · 1 คีย์200 · 1 คีย์controller
GET /admin ไม่​ส่ง​รหัส​ผ่าน401 · 0401 · 5entry-point
GET /admin รหัส​ผ่าน​ผิด401 · 0401 · 5entry-point
GET /admin สิทธิ์​ไม่​พอ403 · 0403 · 5denied-handler

ชุด​คีย์​ของ​สาม​ช่อง​ล่าง​เท่า​กัน​ทั้ง​สาม​ช่อง คือ instance kaenWrittenBy status title type

รหัส​ตอบ​กลับ​ไม่​ขยับ​สัก​ช่อง · สิ่ง​ที่​เปลี่ยน​คือ​มี​เนื้อ​ความ​ให้​ผู้​เรียก​อ่าน

สัญญา​หนึ่ง​ข้อ แต่​มี​ผู้​เขียน​สอง​ราย

ช่อง​ขวา​สุด​ของ​ตาราง​คือ​คำ​ตอบ​ของ​บท​นี้ · สอง​ช่อง​บน​ถูก​เขียน​โดย​ตัว​หนึ่ง ช่อง​ล่าง​ถูก​เขียน​โดย​อีก​ตัว​หนึ่ง · สอง​ตัว​นี้​ไม่​แชร์​อะไร​กัน​เลย

ทำไม​สัญญา​ข้อ​นี้​ถึง​พัง​เงียบ​ตั้งแต่​แรก

คน​ที่​รู้​ว่า body หาย​ไป มัก​เขียน AuthenticationEntryPoint ตัว​เดียว​แล้ว​จบ เพราะ 401 คือ​สิ่ง​ที่​เขา​เห็น​บ่อย​ที่สุด

ผล​คือ 401 กลับ​มา​มี​เนื้อ​ความ ส่วน 403 ยัง​ว่าง​อยู่ · และ​ไม่มี​อะไร​เตือน เพราะ​ทั้ง​สอง​ทาง​ไม่​ได้​ผ่าน​ที่​เดียวกัน

ลงมือ​ที่​เดียว​แล้ว​เห็น​ครึ่ง​เดียว​ดี​ขึ้น อ่าน​เหมือน​แก้​เสร็จ​แล้ว

ของ​ใหม่​ต้อง​ไม่​ไป​แย่ง​ปลาย​ทาง​ที่​ไม่ใช่​ของ​มัน

แถว​แรก​ของ​ตาราง​คือ GET /boom ซึ่ง​ยัง​ได้ 500 พร้อม​คีย์​ครบ​ห้า​ตัว​และ marker ของ advice เหมือน​เดิม​ทุก​ช่อง

ถ้า​แถว​นั้น​เปลี่ยน แปล​ว่า​ตัว​เขียน​ใหม่​ไป​รับ​เรื่อง​ที่​ไม่ใช่​ของ​มัน แล้ว​เรา​จะ​กลาย​เป็น​คน​ที่ กลบ exception จริง​ด้วย​คำ​ตอบ​ของ security

body กลับ​มา​แล้ว แต่​ยัง​ไม่รู้​ว่า​ระบบ​เชื่อ​อะไร​เกี่ยว​กับ​ผู้​เรียก​คน​นั้น

ทุก​กฎ​ที่​เขียน​มา​ถึง​ตอน​นี้​อ้าง​ถึง role · บท​ถัด​ไป​เปิด​ดู​ว่า​สิ่ง​ที่​ระบบ​ถือ​อยู่​จริง ตรง​กับ​ที่​เรา​ประกาศ​ไว้​หรือ​เปล่า

ที่มา​ของ​ตัวเลข​ใน​บท​นี้
  • Spring Boot 4.1.1 · Spring Security 7.1.1 · Kotlin 2.4.10 · JDK 25
  • รัน​ซ้ำ​เอง​ได้​ด้วย npm run verify:clinic-security · ต้อง​สั่ง scripts/clinic-security/up.sh ก่อน เพราะ​ทรง​ของ​บท​ที่ 7 ต้อง​ใช้​ฐาน​ข้อมูล
  • ทุก​ตัวเลข​ใน​ตาราง​ถูก​ตรึง​ไว้​ที่ src/data/perf/clinic-security.ts และ test/course-clinic-security.test.ts ผูก​ตัวเลข​ใน​บท​เข้า​กับ file นั้น
  • Spring Security — Exception Handling (เข้า​ถึง 2026-09-02) — บทบาท​ของ​สอง​ตัว​เขียน​คำ​ตอบ

เช็กความเข้าใจ — บทที่ 5

ข้อ 1 / 3

ทำไมการเขียน `AuthenticationEntryPoint` ตัวเดียวถึงไม่พอ