ทำไมเมธอด .ajax () ของ jquery ไม่ส่งคุกกี้เซสชันของฉัน


338

หลังจากเข้าสู่ระบบผ่านทาง$.ajax()เว็บไซต์ฉันพยายามส่ง$.ajax()คำขอที่สองไปยังเว็บไซต์นั้น แต่เมื่อฉันตรวจสอบส่วนหัวที่ส่งโดยใช้ FireBug จะไม่มีคุกกี้เซสชันรวมอยู่ในคำขอ

ผมทำอะไรผิดหรือเปล่า?


2
คุกกี้ของ ajax อาจเกิดขึ้นหลังจากเว็บคุกกี้และ FireBug อาจจับคุกกี้หน้าแรกได้
Chris

1
ฉันไม่ได้รับสิ่งที่คุณหมายถึง แต่ฉันสามารถพูดได้ว่าถ้าฉันวาง url คำขอในแถบที่อยู่เบราว์เซอร์และตรวจสอบ Firebug อีกครั้งฉันสามารถดูคุกกี้ใน headres ส่งไปยังเซิร์ฟเวอร์ ทางออกใด ๆ ?
user345625

ดังนั้นฉันคิดว่า ajax จะจัดการเช่นเดียวกับที่เบราว์เซอร์ทำ
user345625

รหัสที่คุณใช้คืออะไร
Dean Harding

เบราว์เซอร์จะยังคงสร้างคุกกี้ที่เซิร์ฟเวอร์กำหนดระหว่างการร้องขอ ajax, jquery หรืออื่น ๆ คุณตรวจสอบการตอบสนองต่อคำขอ ajax และตรวจสอบให้แน่ใจว่าคุกกี้ได้กลับมาจากเซิร์ฟเวอร์แล้ว อาจจะมีปัญหากับรหัสเซิร์ฟเวอร์ดังกล่าวว่ามันไม่ได้ตั้งค่าคุกกี้ ฯลฯ
เดวิด

คำตอบ:


218

AJAX โทรศัพท์จะส่งคุกกี้หาก url ที่คุณโทรอยู่นั้นอยู่ในโดเมนเดียวกับสคริปต์การโทรของคุณ

นี่อาจเป็นปัญหาข้ามโดเมน

บางทีคุณอาจพยายามโทรหา url www.domain-a.comในขณะที่สคริปต์การโทรของคุณเปิดอยู่www.domain-b.com(กล่าวอีกนัยหนึ่ง: คุณทำการโทรข้ามโดเมนในกรณีนี้เบราว์เซอร์จะไม่ส่งคุกกี้ใด ๆ เพื่อปกป้องความเป็นส่วนตัวของคุณ)

ในกรณีนี้ตัวเลือกของคุณคือ:

  • เขียนพร็อกซี่เล็ก ๆ ที่อยู่บนโดเมน - ขและส่งต่อคำขอของคุณไปยังโดเมน - เบราว์เซอร์ของคุณจะอนุญาตให้คุณโทรหาพร็อกซี่เพราะมันอยู่บนเซิร์ฟเวอร์เดียวกับสคริปต์การโทร
    คุณสามารถกำหนดค่าพร็อกซีนี้เพื่อยอมรับชื่อคุกกี้และพารามิเตอร์ค่าที่สามารถส่งไปยังโดเมนได้ แต่สำหรับสิ่งนี้ในการทำงานคุณต้องทราบชื่อของคุกกี้และให้ความสำคัญกับเซิร์ฟเวอร์ของคุณในโดเมนที่ต้องการการรับรองความถูกต้อง
  • หากคุณกำลังดึงวัตถุ JSON ลองใช้คำขอJSONPแทน jQuery รองรับสิ่งเหล่านี้ แต่คุณต้องเปลี่ยนบริการของคุณในโดเมน - เพื่อให้ส่งคืนการตอบสนอง JSONP ที่ถูกต้อง

ดีใจถ้ามันช่วยได้นิดหน่อย


19
นอกจากนี้ยังเป็นที่น่าสังเกตว่าคุกกี้สามารถตั้งค่าเป็นเส้นทางที่เฉพาะเจาะจงดังนั้นหากคุณตั้งค่าคุกกี้ด้วยpath=/somethingและคุณกำลังร้องขอหน้า/anotherจากนั้นคุกกี้จะไม่ถูกส่ง เมื่อคุณร้องขอหน้า/somethingคุกกี้จะถูกส่งไปตามที่คาดไว้ ดังนั้นตรวจสอบรหัสที่ตั้งค่าคุกกี้ด้วย
สไตล์

2
jsonp ส่ง coockies ออกมาทำไม
albanx

1
@albanx ใช่ถ้ามีการตั้งข้อกำหนดที่ฉันกล่าวไว้ มันเป็นเพียงคำขอปกติที่เหมือน ๆ กันและเป็นเช่นนั้นส่งคุกกี้
ไข้หวัดใหญ่

1
@albanx นี้คำถามอื่น ๆ ที่เกี่ยวข้องรวมถึงตัวอย่างของวิธีการที่จะทำคำขอ JSONP กับคุกกี้ที่กำหนดเอง
AntonioHerraizS

4
ตามJSONPบนวิกิพีเดีย> วิธีการนี้ถูกทอดทิ้งในความโปรดปรานของ CORS
Peter Dotchev

388

ฉันทำงานในสถานการณ์ข้ามโดเมน ระหว่างการล็อกอินรีโมตเซิร์ฟเวอร์กำลังส่งคืนส่วนหัว Set-Cookie พร้อมกับAccess-Control-Allow-Credentialsตั้งค่าเป็นจริง

การโทร ajax ครั้งต่อไปไปยังเซิร์ฟเวอร์ระยะไกลควรใช้คุกกี้นี้

CORS Access-Control-Allow-Credentialsอยู่ที่นั่นเพื่ออนุญาตการบันทึกข้ามโดเมน ตรวจสอบhttps://developer.mozilla.org/En/HTTP_access_controlเพื่อดูตัวอย่าง

สำหรับฉันดูเหมือนว่าข้อบกพร่องใน JQuery (หรืออย่างน้อยคุณสมบัติที่จะเป็นในรุ่นถัดไป)

UPDATE:

  1. คุกกี้ไม่ได้ถูกตั้งค่าโดยอัตโนมัติจากการตอบกลับ AJAX (การอ้างอิง: http://aleembawany.com/2006/11/14/anatomy-of-a-well-designed-ajax-login-experience/ )

    ทำไม?

  2. คุณไม่สามารถรับค่าคุกกี้จากการตอบสนองเพื่อตั้งค่าด้วยตนเอง ( http://www.w3.org/TR/XMLHttpRequest/#dom-xmlhttprequest-getresponseheader )

    ฉันสับสน ..

    ควรมีวิธีขอjquery.ajax()ตั้งค่าXMLHttpRequest.withCredentials = "true"พารามิเตอร์

คำตอบ: คุณควรใช้xhrFieldsพารามิเตอร์ของhttp://api.jquery.com/jQuery.ajax/

ตัวอย่างในเอกสารประกอบคือ:

$.ajax({
   url: a_cross_domain_url,
   xhrFields: {
      withCredentials: true
   }
});

สิ่งสำคัญคือเซิร์ฟเวอร์ต้องตอบคำขอนี้ให้ถูกต้อง คัดลอกความคิดเห็นที่ดีที่นี่จาก @ Frédéricและ @Pebbl:

Important note: when responding to a credentialed request, server must specify a domain, and cannot use wild carding. The above example would fail if the header was wildcarded as: Access-Control-Allow-Origin: *

ดังนั้นเมื่อคำขอคือ:

Origin: http://foo.example
Cookie: pageAccess=2

เซิร์ฟเวอร์ควรตอบกลับด้วย:

Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Credentials: true

[payload]

มิฉะนั้นส่วนของข้อมูลจะไม่ถูกส่งกลับไปที่สคริปต์ ดู: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials


8
เยี่ยมมาก! ฉันจะใช้ส่วนนี้ + ตั้งค่า Access-Control-Allow-Credentials ให้เป็น True ทางฝั่งเซิร์ฟเวอร์
4324 Frédéric

และฉันจะตั้งค่าข้อมูลรับรองเหล่านั้นได้ที่ไหนที่ Header Autorization ที่หน่วยคำขอ
Francisco Corrales Morales

3
ขอบคุณสำหรับคำตอบ :) เพียงแค่เพิ่มเข้าไปอย่างรวดเร็วมันอาจคุ้มค่าที่จะกล่าวถึงImportant note: when responding to a credentialed request, server must specify a domain, and cannot use wild carding. The above example would fail if the header was wildcarded as: Access-Control-Allow-Origin: * developer.mozilla.org/en-US/docs/Web/HTTP/…
Pebbl

สิ่งนี้ไม่ได้ผลสำหรับฉันอย่างน่าเสียดาย ถ้าฉันเรียกใช้คำร้องขอเดียวกันจาก AngularJS มันใช้งานได้ แต่จาก jQuery ถึงแม้จะมีคำแนะนำเหล่านี้คุกกี้เซสชันก็จะไม่ถูกส่งผ่าน (jQuery v2.1.1)
geoidesic

(OO) คุณช่วยฉันจากหลายชั่วโมงที่เจ็บปวด ช่างเป็นคำตอบที่สมบูรณ์แบบ! ขอบคุณ! ฉันต้องการที่จะเพิ่มสิ่งเหล่านี้ในรูตสาธารณะ. htaccess ของเว็บไซต์ของฉัน: <IfModule mod_headers.c> ส่วนหัวชุดการควบคุมการเข้าถึงอนุญาต - กำเนิด - " localhost " ส่วนหัวชุดการเข้าถึงการควบคุมการอนุญาต - ข้อมูลประจำตัว "จริง" </IfModule>
Vinay Vissh

48

การใช้

xhrFields: { withCredentials:true }

ในฐานะที่เป็นส่วนหนึ่งของการโทร jQuery ajax ของฉันเป็นเพียงส่วนหนึ่งของโซลูชัน ฉันยังต้องให้ส่วนหัวกลับมาในการตอบสนองตัวเลือกจากทรัพยากรของฉัน:

Access-Control-Allow-Origin : http://www.wombling.com
Access-Control-Allow-Credentials : true

เป็นสิ่งสำคัญที่อนุญาต "แหล่งกำเนิด" หนึ่งเดียวเท่านั้นที่อยู่ในส่วนหัวการตอบสนองของการเรียก OPTIONS ไม่ใช่ "*" ฉันทำสิ่งนี้ได้โดยการอ่านที่มาจากคำร้องขอและเติมกลับเข้าไปในคำตอบ - อาจหลีกเลี่ยงเหตุผลดั้งเดิมสำหรับข้อ จำกัด แต่ในกรณีที่ใช้งานของฉันความปลอดภัยไม่ได้เป็นสิ่งสำคัญที่สุด

ฉันคิดว่ามันคุ้มค่าที่จะกล่าวถึงข้อกำหนดสำหรับแหล่งกำเนิดเดียวเท่านั้นเนื่องจากมาตรฐาน W3C ไม่อนุญาตให้มีรายการที่คั่นด้วยช่องว่าง - แต่ Chrome ไม่ได้! http://www.w3.org/TR/cors/#access-control-allow-origin-response-header NB บิต "ในทางปฏิบัติ"


41

ใส่สิ่งนี้ในฟังก์ชั่น init ของคุณ:

$.ajaxSetup({
  xhrFields: {
    withCredentials: true
  }
});

มันจะทำงาน.


1
คุณบันทึกวันของฉัน! ในระดับวิธี withCredentials ไม่ทำงานสำหรับฉัน แต่ในที่สุดมันก็ใช้งานได้! ขอบคุณ
Paulius Matulionis

ระวังด้วยเพราะจะส่งคุกกี้ไปยังคำขอทั้งหมดอีเธอร์สำหรับโดเมนอื่น ๆ (ซึ่งไม่ได้คาดหวังสิ่งนี้และคำขอที่ล้มเหลวโดยต้องการการควบคุมการอนุญาตให้เข้าถึงข้อมูลประจำตัว)
gdbdable

12

มีคำตอบที่ดีสำหรับคำถามนี้อยู่แล้ว แต่ฉันคิดว่าอาจเป็นประโยชน์ในการชี้แจงกรณีที่คุณคาดว่าจะส่งคุกกี้เซสชันเนื่องจากโดเมนคุกกี้ตรงกัน แต่ไม่ได้รับเนื่องจากคำขอ AJAX เป็น ถูกทำให้เป็นโดเมนย่อยอื่น ในกรณีนี้ฉันมีคุกกี้ที่กำหนดให้กับโดเมน* .mydomain.comและฉันต้องการให้รวมอยู่ในคำขอ AJAX ไปยังdifferent.mydomain.com "โดยค่าเริ่มต้นคุกกี้จะไม่ได้รับการส่ง คุณไม่จำเป็นต้องปิดการใช้งาน HTTPONLY บนคุกกี้เซสชั่นเพื่อแก้ไขปัญหานี้คุณจะต้องทำสิ่งที่แนะนำให้ใช้ wombling ( https://stackoverflow.com/a/23660618/545223 ) และทำสิ่งต่อไปนี้

1) เพิ่มสิ่งต่อไปนี้ในคำขอ ajax ของคุณ

xhrFields: { withCredentials:true }

2) เพิ่มส่วนต่อไปนี้ในส่วนหัวการตอบกลับของคุณสำหรับทรัพยากรในโดเมนย่อยที่ต่างกัน

Access-Control-Allow-Origin : http://original.mydomain.com
Access-Control-Allow-Credentials : true

7

หลังจากลองใช้วิธีแก้ไขปัญหาอื่นและยังไม่สามารถใช้งานได้ฉันพบว่าปัญหาคืออะไรในกรณีของฉัน ฉันเปลี่ยน contentType จาก "application / json" เป็น "text / plain"

$.ajax(fullUrl, {
    type: "GET",
    contentType: "text/plain",
    xhrFields: {
         withCredentials: true
    },
    crossDomain: true
});

4

ฉันมีปัญหาเดียวกันนี้และทำการตรวจสอบบางอย่างสคริปต์ของฉันเป็นเพียงแค่ไม่ได้รับคุกกี้ sessionid

ฉันคิดออกโดยดูที่ค่าเซสชันคุกกี้ในเบราว์เซอร์ที่กรอบงานของฉัน (Django) ผ่านคุกกี้เซสชันที่มี HttpOnly เป็นค่าเริ่มต้น นี่หมายความว่าสคริปต์ไม่สามารถเข้าถึงค่า sessionid ดังนั้นจึงไม่ส่งผ่านไปพร้อมกับคำขอ ชนิดที่ไร้สาระที่ HttpOnly จะเป็นค่าเริ่มต้นเมื่อมีหลายสิ่งที่ใช้ Ajax ซึ่งจะต้องมีการ จำกัด การเข้าถึง

ในการแก้ไขปัญหานี้ฉันได้เปลี่ยนการตั้งค่า (SESSION_COOKIE_HTTPONLY = False) แต่ในกรณีอื่น ๆ อาจเป็นสถานะ "HttpOnly" บนเส้นทางคุกกี้


2
อย่าทำอย่างนี้. อนุญาตให้สคริปต์ฝั่งไคลเอ็นต์เข้าถึงเซสชันคุกกี้ซึ่งเป็นเวกเตอร์การโจมตี XSS ที่พบบ่อยที่สุด owasp.org/index.php/HttpOnly
Jason Elkin

1

หากคุณกำลังพัฒนาlocalhostหรือพอร์ตบน localhost เช่นlocalhost:8080นอกเหนือจากขั้นตอนที่อธิบายไว้ในคำตอบข้างต้นคุณต้องให้แน่ใจว่าคุณไม่ได้ผ่านค่าโดเมนในส่วนหัว Set-Cookie
คุณไม่สามารถตั้งค่าโดเมนเป็นlocalhostในส่วนหัว Set-Cookie - ไม่ถูกต้อง - เพียงแค่ละเว้นโดเมน

ดูคุกกี้บน localhost ที่มีโดเมนชัดเจนและทำไม asp.net จะไม่สร้างคุกกี้ใน localhost


0

เพียง 2 เซ็นต์ของฉันในการตั้งค่าปัญหาคุกกี้ PHPSESSID เมื่อใน localhost และภายใต้สภาพแวดล้อมที่ dev ฉันโทร AJAX ไปยังปลายทาง REST API บน locahost บอกว่าที่อยู่ของมันคือmysite.localhost/api/member/login/(โฮสต์ที่ดีในสภาพแวดล้อม dev ของฉัน)

  • เมื่อฉันทำคำขอนี้กับบุรุษไปรษณีย์สิ่งต่าง ๆ ดีและ PHPSESSID ถูกตั้งค่าด้วยการตอบสนอง

  • เมื่อฉันขอปลายทางนี้ผ่าน AJAX จากหน้าพร็อกซีของ Browsersync (เช่นจาก122.133.1.110:3000/test/api/login.phpในบรรทัดที่อยู่ของเบราว์เซอร์ของฉันให้ดูโดเมนนั้นแตกต่างจาก vs mysite.localhost) PHPSESSID ไม่ปรากฏในคุกกี้

  • เมื่อฉันขอนี้โดยตรงจากหน้าในโดเมนเดียวกัน (เช่นmysite.localhost/test/api/login.php) PHPSESSID ตั้งค่าได้ดี

ดังนั้นนี่คือปัญหาคุกกี้คำขอข้ามต้นทางที่กล่าวถึงใน @flu คำตอบด้านบน


0

การเพิ่มสถานการณ์และโซลูชันของฉันในกรณีที่ช่วยเหลือคนอื่น ฉันพบกรณีที่คล้ายกันเมื่อใช้ RESTful API เว็บเซิร์ฟเวอร์ของฉันโฮสต์ไฟล์ HTML / Script / CSS และ Application Server ที่เปิดเผย APIs นั้นโฮสต์บนโดเมนเดียวกัน อย่างไรก็ตามเส้นทางนั้นแตกต่างกัน

เว็บเซิร์ฟเวอร์ - mydomain / หน้าเว็บ /abc.html

ใช้abc.jsซึ่งตั้งค่าคุกกี้ชื่อmycookie

เซิร์ฟเวอร์แอป - mydomain / webapis / servicename

สาย api ที่ถูกทำให้

ฉันคาดหวังว่าคุกกี้ใน mydomain / webapis / servicename และพยายามอ่านมัน แต่มันไม่ได้ถูกส่ง หลังจากอ่านความคิดเห็นจากคำตอบฉันตรวจสอบในเครื่องมือพัฒนาของเบราว์เซอร์ที่เส้นทางของ mycookie ถูกตั้งค่าเป็น "/ เว็บเพจ " และด้วยเหตุนี้จึงไม่สามารถใช้งานได้ในบริการโทรถึง

mydomain / webapis / servicename

ดังนั้นในขณะที่การตั้งค่าคุกกี้จาก jquery นี่คือสิ่งที่ฉันทำ -

$.cookie("mycookie","mayvalue",{**path:'/'**});

-5

บางทีอาจจะไม่ตอบคำถาม 100% แต่ฉันก็สะดุดกับกระทู้นี้ด้วยความหวังว่าจะได้แก้ปัญหาเซสชันเมื่อ ajax โพสต์ไฟล์อัพโหลดจากผู้จัดการสินทรัพย์ของ ในที่สุดการแก้ปัญหาก็ง่าย: พวกเขามีแฟลชอัพโหลด ปิดการใช้งานนั้น (การตั้งค่า

var flashUpload = false;   

ใน asset.php) และไฟก็เริ่มกระพริบอีกครั้ง

เนื่องจากปัญหาเหล่านี้อาจแก้ไขได้ยากมากฉันพบว่าการใส่สิ่งต่อไปนี้ในตัวจัดการการอัปโหลดจะทำให้คุณ (ถูกต้องฉันในกรณีนี้) ในการติดตามที่ถูกต้อง:

$sn=session_name();
error_log("session_name: $sn ");

if(isset($_GET[$sn])) error_log("session as GET param");
if(isset($_POST[$sn])) error_log("session as POST param");
if(isset($_COOKIE[$sn])) error_log("session as Cookie");
if(isset($PHPSESSID)) error_log("session as Global");

ดำดิ่งลงสู่ล็อกแล้วฉันก็พบเซสชั่นที่หายไปอย่างรวดเร็วซึ่งไม่มีการส่งคุกกี้


ฉันไม่คิดว่าตัวอย่างข้างต้นจะได้ผลเพราะเมื่อไม่มีคุกกี้เซสชันค่าของ $ sn จะเป็นเท่าไหร่ (แบบสุ่มหรืออาจเป็นโมฆะ) หรือผู้ใช้สามารถตั้งค่า session_name จากค่า GET เช่นsession_name(isset($_GET['sess']) ? $_GET['sess'] : null);session_start();นี้พวกเขาจะได้รับสิ่งที่ใช้งานได้
Steel Brain

นั่นคือสิ่งที่ฉันพบปัญหา: ไม่มีเซสชั่นเมื่อโพสต์จากผู้อัปโหลดแฟลชสิ่งนี้ เนื่องจากการใช้ตัวระบุเซสชันตัวแปร GET เป็นความคิดที่ไม่ดีและคุกกี้ไม่ทำงานฉันจึงโยนมันออกไป ใครเป็นคนให้ความสนใจแฟลชเป็นเรื่องของอดีตที่ผ่านมา
Ellert van Koperen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.