הגבלת הגישה ל-API באמצעות מפתחות API

אתם יכולים להשתמש במפתחות API כדי להגביל את הגישה לשיטות ספציפיות של API או לכל השיטות ב-API. בדף הזה מוסבר איך להגביל את הגישה ל-API ללקוחות שיש להם מפתח API, וגם איך ליצור מפתח API.

Extensible Service Proxy (ESP) משתמש ב-Service Control API כדי לאמת מפתח API ואת השיוך שלו ל-API מופעל של פרויקט. אם הגדרתם דרישה למפתח API ב-API שלכם, בקשות לשיטה, למחלקה או ל-API המוגנים יידחו אלא אם יש להן מפתח שנוצר בפרויקט שלכם או בפרויקטים אחרים ששייכים למפתחים שהענקתם להם גישה להפעלת ה-API. הפרויקט שבו נוצר מפתח ה-API לא מתועד ולא נוסף לכותרת הבקשה. עם זאת, אפשר לראות את הפרויקט שאליו משויך לקוח מסוים בEndpoints > Service, כמו שמתואר במאמר סינון לפי פרויקט צרכן ספציפי. Google Cloud

במאמר שיתוף ממשקי API שמוגנים באמצעות מפתח API מוסבר באיזה פרויקט צריך ליצור מפתח API. Google Cloud

כברירת מחדל בשירותי gRPC, כל שיטות ה-API דורשות מפתח API כדי לגשת אליהן. אפשר להשבית את הדרישה למפתח API לכל ה-API או לשיטות ספציפיות. כדי לעשות את זה, מוסיפים קטע usage להגדרת השירות ומגדירים rules and selectors, כמו שמתואר בהמשך.

הגבלת הגישה לכל שיטות ה-API או מתן גישה לכל שיטות ה-API

כדי לציין שלא נדרש מפתח API כדי לגשת ל-API שלכם:

  1. פותחים את קובץ הגדרת שירות gRPC של הפרויקט בעורך טקסט ומחפשים או מוסיפים קטע usage.

  2. בקטע usage, מציינים כלל allow_unregistered_calls באופן הבא. תו כללי "*" ב-selector מציין שהכלל חל על כל ה-methods ב-API.

    usage:
      rules:
      # All methods can be called without an API Key.
      - selector: "*"
        allow_unregistered_calls: true
    

הסרת הגבלה של מפתח API משיטה

כדי להשבית את האימות של מפתח API לשיטה מסוימת, גם אם הגבלתם את הגישה ל-API:

  1. פותחים את קובץ הגדרת השירות של gRPC של הפרויקט בכלי לעריכת טקסט ומוצאים או מוסיפים קטע usage:

  2. בקטע usage, מציינים כלל allow_unregistered_calls באופן הבא. הסימן selector מציין שהכלל חל רק על השיטה שצוינה – במקרה הזה, ListShelves.

    usage:
      rules:
      # ListShelves method can be called without an API Key.
      - selector: endpoints.examples.bookstore.Bookstore.ListShelves
        allow_unregistered_calls: true
    

שליחת קריאה ל-API באמצעות מפתח API

האופן שבו קוראים ל-API משתנה בהתאם לסוג הלקוח: gRPC או HTTP.

לקוחות gRPC

אם מתודה דורשת מפתח API, לקוחות gRPC צריכים לקבוע ערך (pass) את ערך המפתח כx-api-key מטא-נתונים עם הפעלת method.

Python

def run(
    host, port, api_key, auth_token, timeout, use_tls, servername_override, ca_path

Java

private static final class Interceptor implements ClientInterceptor {
  private final String apiKey;
  private final String authToken;

  private static Logger LOGGER = Logger.getLogger("InfoLogging");

  private static Metadata.Key<String> API_KEY_HEADER =
      Metadata.Key.of("x-api-key", Metadata.ASCII_STRING_MARSHALLER);
  private static Metadata.Key<String> AUTHORIZATION_HEADER =
      Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);

  public Interceptor(String apiKey, String authToken) {
    this.apiKey = apiKey;
    this.authToken = authToken;
  }

  @Override
  public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
      MethodDescriptor<ReqT,RespT> method, CallOptions callOptions, Channel next) {
    LOGGER.info("Intercepted " + method.getFullMethodName());
    ClientCall<ReqT, RespT> call = next.newCall(method, callOptions);

    call = new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(call) {
      @Override
      public void start(Listener<RespT> responseListener, Metadata headers) {
        if (apiKey != null && !apiKey.isEmpty()) {
          LOGGER.info("Attaching API Key: " + apiKey);
          headers.put(API_KEY_HEADER, apiKey);
        }
        if (authToken != null && !authToken.isEmpty()) {
          System.out.println("Attaching auth token");
          headers.put(AUTHORIZATION_HEADER, "Bearer " + authToken);
        }
        super.start(responseListener, headers);
      }
    };
    return call;
  }
}

המשך

func main() {
	flag.Parse()

	// Set up a connection to the server.
	conn, err := grpc.Dial(*addr, grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(conn)

	if *keyfile != "" {
		log.Printf("Authenticating using Google service account key in %s", *keyfile)
		keyBytes, err := ioutil.ReadFile(*keyfile)
		if err != nil {
			log.Fatalf("Unable to read service account key file %s: %v", *keyfile, err)
		}

		tokenSource, err := google.JWTAccessTokenSourceFromJSON(keyBytes, *audience)
		if err != nil {
			log.Fatalf("Error building JWT access token source: %v", err)
		}
		jwt, err := tokenSource.Token()
		if err != nil {
			log.Fatalf("Unable to generate JWT token: %v", err)
		}
		*token = jwt.AccessToken
		// NOTE: the generated JWT token has a 1h TTL.
		// Make sure to refresh the token before it expires by calling TokenSource.Token() for each outgoing requests.
		// Calls to this particular implementation of TokenSource.Token() are cheap.
	}

	ctx := context.Background()
	if *key != "" {
		log.Printf("Using API key: %s", *key)
		ctx = metadata.AppendToOutgoingContext(ctx, "x-api-key", *key)
	}
	if *token != "" {
		log.Printf("Using authentication token: %s", *token)
		ctx = metadata.AppendToOutgoingContext(ctx, "Authorization", fmt.Sprintf("Bearer %s", *token))
	}

	// Contact the server and print out its response.
	name := defaultName
	if len(flag.Args()) > 0 {
		name = flag.Arg(0)
	}
	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	log.Printf("Greeting: %s", r.Message)
}

Node.js

const makeGrpcRequest = (JWT_AUTH_TOKEN, API_KEY, HOST, GREETEE) => {
  // Uncomment these lines to set their values
  // const JWT_AUTH_TOKEN = 'YOUR_JWT_AUTH_TOKEN';
  // const API_KEY = 'YOUR_API_KEY';
  // const HOST = 'localhost:50051'; // The IP address of your endpoints host
  // const GREETEE = 'world';

  // Import required libraries
  const grpc = require('@grpc/grpc-js');
  const protoLoader = require('@grpc/proto-loader');
  const path = require('path');

  // Load protobuf spec for an example API
  const PROTO_PATH = path.join(__dirname, '/protos/helloworld.proto');

  const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true,
  });

  const protoObj = grpc.loadPackageDefinition(packageDefinition).helloworld;

  // Create a client for the protobuf spec
  const client = new protoObj.Greeter(HOST, grpc.credentials.createInsecure());

  // Build gRPC request
  const metadata = new grpc.Metadata();
  if (API_KEY) {
    metadata.add('x-api-key', API_KEY);
  } else if (JWT_AUTH_TOKEN) {
    metadata.add('authorization', `Bearer ${JWT_AUTH_TOKEN}`);
  }

  // Execute gRPC request
  client.sayHello({name: GREETEE}, metadata, (err, response) => {
    if (err) {
      console.error(err);
    }

    if (response) {
      console.log(response.message);
    }
  });
};

לקוחות HTTP

אם אתם משתמשים בתכונה Cloud Endpoints for gRPC' HTTP transcoding, לקוחות HTTP יכולים לשלוח את המפתח כפרמטר של שאילתה באותו אופן שבו הם עושים זאת עבור שירותי OpenAPI.

שיתוף ממשקי API שמוגנים באמצעות מפתח API

מפתחות API משויכים ל Google Cloud פרויקט שבו הם נוצרו. אם החלטתם לדרוש מפתח API עבור ה-API שלכם, הפרויקט שבו נוצר מפתח ה-API תלוי בתשובות לשאלות הבאות: Google Cloud

  • האם אתם צריכים להבחין בין המתקשרים לממשק ה-API כדי שתוכלו להשתמש בתכונות של Endpoints כמו מכסות?
  • האם לכל מי שמתקשר עם ה-API יש פרויקט משלו ב- Google Cloud ?
  • צריך להגדיר הגבלות שונות על מפתחות API?

עץ ההחלטות הבא יעזור לכם להחליט באיזה פרויקט ליצור את מפתח ה-API. Google Cloud

עץ החלטות בנושא מפתחות API

אישור הרשאה להפעלת ה-API

אם אתם צריכים להבחין בין המתקשרים ל-API שלכם, ולכל מתקשר יש פרויקט משלו ב- Google Cloud , אתם יכולים להעניק לישויות הרשאה להפעיל את ה-API בפרויקט שלהן ב- Google Cloud . Google Cloud כך משתמשים ב-API שלכם יכולים ליצור מפתח API משלהם לשימוש ב-API שלכם.

לדוגמה, נניח שהצוות שלכם יצר API לשימוש פנימי על ידי תוכנות לקוח שונות בחברה, ולכל תוכנת לקוח יש פרויקט משלה. Google Cloud כדי להבחין בין מי שקוראים ל-API שלכם, צריך ליצור את מפתח ה-API של כל אחד מהם בפרויקט Google Cloud שונה. אתם יכולים להעניק לעמיתים שלכם הרשאה להפעיל את ה-API בפרויקט Google Cloud שתוכנית הלקוח משויכת אליו.

כדי לאפשר למשתמשים ליצור מפתח API משלהם:

  1. בפרויקט Google Cloud שבו מוגדר ה-API, מעניקים לכל משתמש את ההרשאה להפעלת ה-API.
  2. צריך לפנות למשתמשים ולעדכן אותם שהם יכולים להפעיל את ה-API בפרויקט שלהם ב- Google Cloudו ליצור מפתח API.

ליצור Google Cloud פרויקט נפרד לכל מתקשר

אם אתם צריכים להבחין בין המתקשרים ל-API שלכם, ולא לכל המתקשרים יש Google Cloud פרויקטים, אתם יכולים ליצור פרויקט Google Cloud נפרד ומפתח API לכל מתקשר. לפני שיוצרים את הפרויקטים, כדאי לחשוב על שמות הפרויקטים כדי שיהיה קל לזהות את המתקשר שמשויך לפרויקט.

לדוגמה, נניח שיש לכם לקוחות חיצוניים של ה-API שלכם, ואין לכם מושג איך נוצרו תוכנות הלקוח שקוראות ל-API שלכם. יכול להיות שחלק מהלקוחות משתמשים בשירותים ויש להם פרויקט, ויכול להיות שלחלק מהם אין. Google Cloud Google Cloud כדי להבחין בין המתקשרים, צריך ליצור Google Cloud פרויקט ומפתח API נפרדים לכל מתקשר.

כדי ליצור Google Cloud פרויקט ומפתח API נפרדים לכל מתקשר:

  1. יוצרים פרויקט נפרד לכל מתקשר.
  2. בכל פרויקט, מפעילים את ה-API ו יוצרים מפתח API.
  3. נותנים את מפתח ה-API לכל מי שמתקשר.

יצירת מפתח API לכל מתקשר

אם אתם לא צריכים להבחין בין קריאות ל-API, אבל אתם רוצים להוסיף הגבלות על מפתח API, אתם יכולים ליצור מפתח API נפרד לכל קורא באותו פרויקט.

כדי ליצור מפתח API לכל קורא באותו פרויקט:

  1. בפרויקט שבו מוגדר ה-API או בפרויקט שבו ה-API מופעל, יוצרים מפתח API לכל לקוח עם ההגבלות על מפתח ה-API שאתם צריכים.
  2. נותנים את מפתח ה-API לכל מי שמתקשר.

יצירת מפתח API אחד לכל המתקשרים

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

כדי ליצור מפתח API אחד לכל המתקשרים:
  1. בפרויקט שבו מוגדר ה-API או בפרויקט שבו ה-API מופעל, יוצרים מפתח API לכל המתקשרים עם ההגבלות על מפתח ה-API שנדרשות.
  2. נותנים את אותו מפתח API לכל מי שמתקשר.

הגבלות על אפליקציות

הגבלות על אפליקציות מציינות אילו כתובות IP, אפליקציות ואתרים יכולים להשתמש במפתח ה-API. מידע נוסף זמין במאמר הוספת הגבלות על אפליקציות.

הערה: אם משתמשים בHTTP referrers (websites) כהגבלת אפליקציה, צריך לכלול סכימה (לדוגמה, https://) כשמוסיפים את הגבלת האתר. לדוגמה, https://example.com/* היא הגבלה תקינה, אבל example.com/* היא לא.

המאמרים הבאים