הוספת תמיכה ב-CORS ל-proxy ל-API

הדף הזה רלוונטי ל-Apigee ול-Apigee Hybrid.

לעיון במסמכי התיעוד של Apigee Edge

שיתוף משאבים בין מקורות (CORS) הוא מנגנון סטנדרטי שמאפשר לקריאות JavaScript XMLHttpRequest ‏ (XHR) שמופעלות בדף אינטרנט ליצור אינטראקציה עם משאבים מדומיינים שאינם מקור. CORS הוא פתרון נפוץ למדיניות המקור הזהה שנאכפת על ידי כל הדפדפנים. לדוגמה, אם שולחים קריאת XHR ל-Twitter API מקוד JavaScript שמופעל בדפדפן, הקריאה תיכשל. הסיבה לכך היא שהדומיין שממנו הדף מוצג בדפדפן שלכם לא זהה לדומיין שממנו מוצג Twitter API. CORS מספק פתרון לבעיה הזו בכך שהוא מאפשר לשרתים להביע הסכמה אם הם רוצים לספק שיתוף משאבים בין מקורות.

תרחיש שימוש אופייני ב-CORS

הקוד הבא של JQuery קורא לשירות יעד פיקטיבי. אם הפקודה מופעלת מתוך ההקשר של דפדפן (דף אינטרנט), היא תיכשל בגלל מדיניות המקור הזהה:

<script>
var url = "http://service.example.com";
$(document).ready(function(){
  $("button").click(function(){
    $.ajax({
        type:"GET",
        url:url,
        async:true,
        dataType: "json",
           success: function(json) {
              // Parse the response.
              // Do other things.
           },
           error: function(xhr, status, err) {
              // This is where we end up!
            }
    });
  });
});
</script>

אחת הדרכים לפתור את הבעיה היא ליצור proxy ל-API של Apigee שמבצע קריאה ל-API של השירות בקצה העורפי. חשוב לזכור ש-Apigee נמצא בין הלקוח (דפדפן במקרה הזה) לבין ה-API של הקצה העורפי (השירות). מכיוון ש-proxy ל-API פועל בשרת ולא בדפדפן, הוא יכול לקרוא לשירות בהצלחה. לאחר מכן, כל מה שצריך לעשות הוא לצרף כותרות CORS לתגובה של TargetEndpoint. כל עוד הדפדפן תומך ב-CORS, הכותרות האלה מאותתות לדפדפן שמותר לו להרחיב את מדיניות המקורות הזהים שלו, וכך לאפשר לקריאה ל-API ממקורות שונים להצליח.

אחרי שיוצרים את ה-proxy עם תמיכה ב-CORS, אפשר לקרוא לכתובת ה-URL של ה-proxy ל-API במקום לשירות לקצה העורפי בקוד בצד הלקוח. לדוגמה:

<script>
var url = "http://myorg-test.apigee.net/v1/example";
$(document).ready(function(){
  $("button").click(function(){
    $.ajax({
        type:"GET",
        url:url,
        async:true,
        dataType: "json",
           success: function(json) {
              // Parse the response.
              // Do other things.
           },
           error: function(xhr, status, err) {
              // This time, we do not end up here!
            }
    });
  });
});
</script>

צירוף מדיניות ה-CORS ל-PreFlow של ProxyEndpoint

צירוף מדיניות Add CORS לשרת proxy חדש של API

כדי להוסיף תמיכה ב-CORS ל-proxy ל-API, צריך לצרף מדיניות Add CORS ל-proxy ל-API.

המדיניות Add CORS מוסיפה את הכותרות המתאימות לתגובה. בעצם, הכותרות מאפשרות לדפדפן לדעת עם אילו מקורות הוא ישתף את המשאבים שלו, אילו שיטות הוא מקבל וכן הלאה. מידע נוסף על כותרות ה-CORS האלה זמין בהמלצה של W3C בנושא שיתוף משאבים בין מקורות.

צריך לשנות את המדיניות באופן הבא:

  • מוסיפים את הכותרות content-type ו-authorization (נדרשות לתמיכה באימות בסיסי או ב-OAuth2) לכותרת Access-Control-Allow-Headers, כמו שמוצג בקטע הקוד שלמטה.
  • באימות OAuth2, יכול להיות שתצטרכו לבצע פעולות כדי לתקן התנהגות שלא תואמת ל-RFC.
<CORS continueOnError="false" enabled="true" name="add-cors">
    <DisplayName>Add CORS</DisplayName>
    <AllowOrigins>{request.header.origin}</AllowOrigins>
    <AllowMethods>GET, PUT, POST, DELETE</AllowMethods>
    <AllowHeaders>origin, x-requested-with, accept, content-type, authorization</AllowHeaders>
    <ExposeHeaders>*</ExposeHeaders>
    <MaxAge>3628800</MaxAge>
    <AllowCredentials>false</AllowCredentials>
    <GeneratePreflightResponse>true</GeneratePreflightResponse>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</CORS>

הוספת כותרות CORS לשרת proxy קיים

כדי להוסיף את מדיניות ה-CORS ל-proxy ל-API קיים:

  1. במסוף Google Cloud , נכנסים לדף Apigee > Proxy Development > API Proxies.

    מעבר אל API Proxies

  2. בוחרים את ה-proxy ל-API שאליו רוצים להוסיף את מדיניות ה-CORS. פרטי ה-proxy מוצגים בממשק המשתמש של מסוף Google Cloud .
  3. לוחצים על הכרטיסייה פיתוח.
  4. בחלונית הניווט, לוחצים על הלחצן + בשורה Policies (מדיניות).
  5. בתיבת הדו-שיח Create policy (יצירת מדיניות), לוחצים על השדה Select policy (בחירת מדיניות), גוללים למטה אל Security (אבטחה) ובוחרים באפשרות CORS.

  6. מזינים את פרטי המדיניות ולוחצים על יצירה.

  7. בחלונית הניווט, לוחצים על PreFlow בקטע Target Endpoints > default.
  8. לוחצים על הלחצן + לצד PreFlow בחלונית Request של העורך החזותי.
  9. בתיבת הדו-שיח Add policy step (הוספת מדיניות), בוחרים במדיניות CORS.
  10. לוחצים על הוספה כדי לצרף את המדיניות.

טיפול בבקשות קדם-הפעלה של CORS

בקשת קדם-הפעלה של CORS היא בקשה שנשלחת לשרת כדי לוודא שהוא תומך ב-CORS. תשובות אופייניות לבקשות קדם-הפעלה כוללות את המקורות שהשרת יקבל מהם בקשות CORS, רשימה של שיטות HTTP שנתמכות בבקשות CORS, כותרות שאפשר להשתמש בהן כחלק מבקשת המשאב, הזמן המקסימלי שבו תשובת קדם-ההפעלה תישמר במטמון ועוד. אם השירות לא מציין תמיכה ב-CORS או לא רוצה לקבל בקשות ממקורות שונים מהמקור של הלקוח, מדיניות המקורות השונים של הדפדפן תיאכף וכל הבקשות בין דומיינים שהלקוח שולח כדי ליצור אינטראקציה עם משאבים שמארחים בשרת הזה ייכשלו.

בדרך כלל, בקשות קדם-הפעלה של CORS מבוצעות באמצעות ה-method‏ HTTP OPTIONS. כששרת שתומך ב-CORS מקבל בקשת OPTIONS, הוא מחזיר ללקוח קבוצה של כותרות CORS שמציינות את רמת התמיכה שלו ב-CORS. כתוצאה מהלחיצת יד הזו, הלקוח יודע מה מותר לו לבקש מהדומיין שאינו המקור.

מידע נוסף על בקשות Preflight מופיע בהמלצה של W3C בנושא שיתוף משאבים בין מקורות (CORS). בנוסף, יש בלוגים ומאמרים רבים בנושא CORS שאפשר לעיין בהם.

‫Apigee לא כולל פתרון קדם-הפעלה של CORS כברירת מחדל, אבל אפשר להטמיע אותו כמו שמתואר בסעיף הזה. המטרה היא שה-proxy יעריך בקשת OPTIONS בתהליך מותנה. לאחר מכן, ה-Proxy יכול לשלוח תגובה מתאימה בחזרה ללקוח.

הנה תרשים זרימה לדוגמה, ואחריו הסבר על החלקים שמטפלים בבקשת הבדיקה לפני ההפעלה:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProxyEndpoint name="default">
    <Description/>
    <Flows>
        <Flow name="OptionsPreFlight">
            <Request>
                <Step>
                    <Name>add-cors</Name>
                </Step>
            </Request>
            <Response/>
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
        </Flow>
    </Flows>

    <PreFlow name="PreFlow">
        <Request/>
        <Response/>

    </PreFlow>
    <HTTPProxyConnection>
        <BasePath>/v1/cnc</BasePath>
        <VirtualHost>default</VirtualHost>
        <VirtualHost>secure</VirtualHost>
    </HTTPProxyConnection>
    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
    <RouteRule name="default">
        <TargetEndpoint>default</TargetEndpoint>
   </RouteRule>
   <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
</ProxyEndpoint>

החלקים העיקריים של ProxyEndpoint הם:

  • נוצר RouteRule ליעד NULL עם תנאי לבקשת OPTIONS. שימו לב: לא צוין TargetEndpoint. אם מתקבלת בקשת OPTIONS וכותרות הבקשה Origin ו-Access-Control-Request-Method אינן null, ה-proxy מחזיר מיד את כותרות ה-CORS בתגובה ללקוח (תוך דילוג על יעד ברירת המחדל בפועל של ה-backend). פרטים על תנאי זרימה ו-RouteRule זמינים במאמר תנאים עם משתני זרימה.
    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
  • נוצר רצף פעולות OptionsPreFlight שמוסיף מדיניות CORS, שמכילה את כותרות ה-CORS, לרצף הפעולות אם מתקבלת בקשת OPTIONS וכותרות הבקשה Origin ו-Access-Control-Request-Method אינן null.
     <Flow name="OptionsPreFlight">
                <Request>
                    <Step>
                        <Name>add-cors</Name>
                    </Step>
                </Request>
                <Response/>
            <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
     </Flow>