참고: Python 2.7은 2024년 1월 31일 지원 종료됩니다. 기존 Python 2.7 애플리케이션을 계속 실행하고 트래픽을 받을 수 있습니다. 그러나 지원 종료 날짜 이후에는 해당 런타임을 사용하는 애플리케이션의 재배포를 App Engine에서 차단할 수 있습니다.
지원되는 최신 Python 버전으로 마이그레이션하는 것이 좋습니다.
REGION_ID는 앱을 만들 때 선택한 리전을 기준으로 Google에서 할당하는 축약된 코드입니다. 일부 리전 ID는 일반적으로 사용되는 국가 및 주/도 코드와 비슷하게 표시될 수 있지만 코드는 국가 또는 주/도와 일치하지 않습니다. 2020년 2월 이후에 생성된 앱의 경우 REGION_ID.r이 App Engine URL에 포함됩니다. 이 날짜 이전에 만든 기존 앱의 경우 URL에서 리전 ID는 선택사항입니다.
App Identity API를 사용하면 애플리케이션이 스스로의 애플리케이션 ID(다른 명칭은 프로젝트 ID)를 확인할 수 있습니다. App Engine 애플리케이션은 이 ID를 사용하여 다른 App Engine 앱, Google API, 서드 파티 애플리케이션 및 서비스에 자신의 ID를 알릴 수 있습니다. 애플리케이션 ID는 URL 또는 이메일 주소를 생성하거나 런타임을 결정하는 데 사용될 수도 있습니다.
프로젝트 ID 가져오기
프로젝트 ID는 app_identity.get_application_id()메서드를 사용하여 확인할 수 있습니다. WSGI 또는 CGI 환경이 API로 처리되는 일부 구현 세부정보를 노출합니다.
애플리케이션 호스트 이름 가져오기
기본적으로 App Engine 앱은 https://PROJECT_ID.REGION_ID.r.appspot.com 형식의 URL에서 제공되며, 여기서 프로젝트 ID는 호스트 이름의 일부입니다.
앱이 커스텀 도메인에서 제공될 때는 전체 호스트 이름 구성요소를 검색해야 할 수 있습니다. app_identity.get_default_version_hostname() 메서드를 사용하여 이 작업을 수행합니다.
다른 App Engine 앱에 ID 알림
App Engine 앱에 요청하는 App Engine 앱의 ID를 확인하려면 요청 헤더 X-Appengine-Inbound-Appid를 사용하면 됩니다. 이 헤더는 URLFetch 서비스를 통해 요청에 추가되며 사용자가 수정할 수 없으므로 이 헤더가 있으면 요청하는 애플리케이션의 프로젝트 ID라고 신뢰할 수 있습니다.
요구사항:
앱의 appspot.com 도메인에 대한 호출에만 X-Appengine-Inbound-Appid 헤더가 포함됩니다. 커스텀 도메인에 대한 호출은 헤더를 포함하지 않습니다.
리디렉션을 따르지 않도록 요청을 설정해야 합니다.
urlfetch.fetch()follow_redirects 매개변수를 False로 설정합니다.
애플리케이션 핸들러에서 X-Appengine-Inbound-Appid 헤더를 읽고 요청을 실행할 수 있는 ID 목록과 비교하여 수신 ID를 확인할 수 있습니다. 예를 들면 다음과 같습니다.
importwebapp2classMainPage(webapp2.RequestHandler):allowed_app_ids=["other-app-id","other-app-id-2"]defget(self):incoming_app_id=self.request.headers.get("X-Appengine-Inbound-Appid",None)ifincoming_app_idnotinself.allowed_app_ids:self.abort(403)self.response.write("This is a protected page.")app=webapp2.WSGIApplication([("/",MainPage)],debug=True)
Google API에 ID 알림
Google API는 인증 및 승인에 OAuth 2.0 프로토콜을 사용합니다. App Identity API는 요청 소스가 애플리케이션 자체라고 어설션하는 데 사용되는 OAuth 토큰을 만들 수 있습니다. get_access_token() 메서드는 특정 범위 또는 범위 목록의 액세스 토큰을 반환합니다. 이 토큰을 호출의 HTTP 헤더에 설정하면 호출하는 애플리케이션을 식별할 수 있습니다.
다음 예시에서는 App Identity API를 사용하여 Cloud Storage API에 인증하고 프로젝트의 모든 버킷을 검색하여 나열하는 방법을 보여줍니다.
importjsonimportloggingfromgoogle.appengine.apiimportapp_identityfromgoogle.appengine.apiimporturlfetchimportwebapp2classMainPage(webapp2.RequestHandler):defget(self):auth_token,_=app_identity.get_access_token("https://www.googleapis.com/auth/cloud-platform")logging.info("Using token {} to represent identity {}".format(auth_token,app_identity.get_service_account_name()))response=urlfetch.fetch("https://www.googleapis.com/storage/v1/b?project={}".format(app_identity.get_application_id()),method=urlfetch.GET,headers={"Authorization":"Bearer {}".format(auth_token)},)ifresponse.status_code!=200:raiseException("Call failed. Status code {}. Body {}".format(response.status_code,response.content))result=json.loads(response.content)self.response.headers["Content-Type"]="application/json"self.response.write(json.dumps(result,indent=2))app=webapp2.WSGIApplication([("/",MainPage)],debug=True)
애플리케이션의 ID는 서비스 계정 이름(일반적으로 applicationid@appspot.gserviceaccount.com)으로 표시됩니다. get_service_account_name() 메서드를 사용하여 정확한 값을 가져올 수 있습니다.
ACL을 제공하는 서비스의 경우 이 계정 액세스 권한을 부여하여 애플리케이션 액세스 권한을 부여할 수 있습니다.
서드 파티 서비스에 ID 알림
get_access_token()으로 생성된 토큰은 Google 서비스에서만 작동합니다. 그러나 내부적인 서명 기술을 사용하면 다른 서비스에 애플리케이션의 ID를 알릴 수 있습니다. sign_blob() 메서드는 애플리케이션에 고유한 비공개 키를 사용하여 바이트에 서명하고 get_public_certificates() 메서드는 서명을 검증하는 데 사용할 수 있는 인증서를 반환합니다.
다음 예에서는 BLOB에 서명하고 서명을 검증하는 방법을 보여줍니다.
importbase64fromCrypto.HashimportSHA256fromCrypto.PublicKeyimportRSAfromCrypto.SignatureimportPKCS1_v1_5fromCrypto.Util.asn1importDerSequencefromgoogle.appengine.apiimportapp_identityimportwebapp2defverify_signature(data,signature,x509_certificate):"""Verifies a signature using the given x.509 public key certificate."""# PyCrypto 2.6 doesn't support x.509 certificates directly, so we'll need# to extract the public key from it manually.# This code is based on https://github.com/google/oauth2client/blob/master# /oauth2client/_pycrypto_crypt.pypem_lines=x509_certificate.replace(b" ",b"").split()cert_der=base64.urlsafe_b64decode(b"".join(pem_lines[1:-1]))cert_seq=DerSequence()cert_seq.decode(cert_der)tbs_seq=DerSequence()tbs_seq.decode(cert_seq[0])public_key=RSA.importKey(tbs_seq[6])signer=PKCS1_v1_5.new(public_key)digest=SHA256.new(data)returnsigner.verify(digest,signature)defverify_signed_by_app(data,signature):"""Checks the signature and data against all currently valid certificates for the application."""public_certificates=app_identity.get_public_certificates()forcertinpublic_certificates:ifverify_signature(data,signature,cert.x509_certificate_pem):returnTruereturnFalseclassMainPage(webapp2.RequestHandler):defget(self):message="Hello, world!"signing_key_name,signature=app_identity.sign_blob(message)verified=verify_signed_by_app(message,signature)self.response.content_type="text/plain"self.response.write("Message: {}\n".format(message))self.response.write("Signature: {}\n".format(base64.b64encode(signature)))self.response.write("Verified: {}\n".format(verified))app=webapp2.WSGIApplication([("/",MainPage)],debug=True)
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["이해하기 어려움","hardToUnderstand","thumb-down"],["잘못된 정보 또는 샘플 코드","incorrectInformationOrSampleCode","thumb-down"],["필요한 정보/샘플이 없음","missingTheInformationSamplesINeed","thumb-down"],["번역 문제","translationIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 2025-05-01(UTC)"],[[["\u003cp\u003eThe \u003ccode\u003eREGION_ID\u003c/code\u003e is a Google-assigned code based on the region selected during app creation, included in App Engine URLs for apps created after February 2020, but it does not directly correspond to specific countries or provinces.\u003c/p\u003e\n"],["\u003cp\u003eThe App Identity API allows applications to find their project ID, which can be used for identity assertion with other App Engine apps, Google APIs, or third-party services, as well as generating URLs or email addresses.\u003c/p\u003e\n"],["\u003cp\u003eApp Engine apps can verify the identity of another App Engine app making a request by checking the \u003ccode\u003eX-Appengine-Inbound-Appid\u003c/code\u003e header, but this is only available for calls to the \u003ccode\u003eappspot.com\u003c/code\u003e domain and requires disabling redirects.\u003c/p\u003e\n"],["\u003cp\u003eThe App Identity API's \u003ccode\u003eget_access_token()\u003c/code\u003e method generates OAuth 2.0 tokens for authentication with Google APIs, while the \u003ccode\u003esign_blob()\u003c/code\u003e and \u003ccode\u003eget_public_certificates()\u003c/code\u003e methods allow identity assertion with non-Google services through unique application-specific key signing.\u003c/p\u003e\n"],["\u003cp\u003eEach application has access to a default Cloud Storage bucket that includes free storage and I/O quota, the name of which can be retrieved via the \u003ccode\u003eget_default_gcs_bucket_name\u003c/code\u003e method.\u003c/p\u003e\n"]]],[],null,["# App Identity API for legacy bundled services\n\n### Region ID\n\nThe \u003cvar translate=\"no\"\u003eREGION_ID\u003c/var\u003e is an abbreviated code that Google assigns\nbased on the region you select when you create your app. The code does not\ncorrespond to a country or province, even though some region IDs may appear\nsimilar to commonly used country and province codes. For apps created after\nFebruary 2020, \u003cvar translate=\"no\"\u003eREGION_ID\u003c/var\u003e`.r` is included in\nApp Engine URLs. For existing apps created before this date, the\nregion ID is optional in the URL.\n\nLearn more\n[about region IDs](/appengine/docs/legacy/standard/python/how-requests-are-routed#region-id). \nOK\n\nThe App Identity API lets an application discover its application ID (also\ncalled the [project ID](https://support.google.com/cloud/answer/6158840)). Using\nthe ID, an App Engine application can assert its identity to other App Engine\nApps, Google APIs, and third-party applications and services. The\napplication ID can also be used to generate a URL or email address, or to make\na run-time decision.\n| This API is supported for first-generation runtimes and can be used when [upgrading to corresponding second-generation runtimes](/appengine/docs/standard/\n| python3\n|\n| /services/access). If you are updating to the App Engine Python 3 runtime, refer to the [migration guide](/appengine/migration-center/standard/migrate-to-second-gen/python-differences) to learn about your migration options for legacy bundled services.\n\nGetting the project ID\n----------------------\n\nThe project ID can be found using the\n\n\n`app_identity.get_application_id()`method. The WSGI or CGI environment exposes\nsome implementation details, which are handled by the API.\n\n\nGetting the application hostname\n--------------------------------\n\nBy default, App Engine apps are served from URLs in the form\n\n`https://`\u003cvar translate=\"no\"\u003ePROJECT_ID\u003c/var\u003e`.`\u003cvar translate=\"no\"\u003e\u003ca href=\"#appengine-urls\" style=\"border-bottom: 1px dotted #999\" class=\"devsite-dialog-button\" data-modal-dialog-id=\"regional_url\" track-type=\"progressiveHelp\" track-name=\"modalHelp\" track-metadata-goal=\"regionalURL\"\u003eREGION_ID\u003c/a\u003e\u003c/var\u003e`.r.appspot.com`, where the project ID is part of the hostname.\nIf an app is served from a custom domain, it may be necessary to retrieve the\nentire hostname component. You can do this using the `app_identity.get_default_version_hostname()` method.\n\nAsserting identity to other App Engine apps\n-------------------------------------------\n\nIf you want to determine the identity of the App Engine app that is making a\nrequest to your App Engine app, you can use the request header\n`X-Appengine-Inbound-Appid`. This header is added to the request by the URLFetch\nservice and is not user modifiable, so it safely indicates the requesting\napplication's project ID, if present.\n\n**Requirements**:\n\n- Only calls made to your app's `appspot.com` domain will contain the `X-Appengine-Inbound-Appid` header. Calls to custom domains do not contain the header.\n- Your requests must be set to not follow redirects. Set the `urlfetch.fetch()` [`follow_redirects`](/appengine/docs/legacy/standard/python/refdocs/google.appengine.api.urlfetch#google.appengine.api.urlfetch.fetch) parameter to `False`.\n\nIn your application handler, you can check the incoming ID by reading the\n`X-Appengine-Inbound-Appid` header and comparing it to a list of IDs allowed\nto make requests. For example: \n\n import webapp2\n\n\n class MainPage(webapp2.RequestHandler):\n allowed_app_ids = [\"other-app-id\", \"other-app-id-2\"]\n\n def get(self):\n incoming_app_id = self.request.headers.get(\"X-Appengine-Inbound-Appid\", None)\n\n if incoming_app_id not in self.allowed_app_ids:\n self.abort(403)\n\n self.response.write(\"This is a protected page.\")\n\n\n app = webapp2.WSGIApplication([(\"/\", MainPage)], debug=True)\n\nAsserting identity to Google APIs\n---------------------------------\n\nGoogle APIs use the OAuth 2.0 protocol for [authentication and\nauthorization](https://developers.google.com/identity/protocols/OAuth2). The\nApp Identity API can create OAuth tokens that can be used to assert that the\nsource of a request is the application itself. The `get_access_token()` method\nreturns an access token for a scope, or list of scopes. This token can then be\nset in the HTTP headers of a call to identify the calling application.\nThe following example shows how to use the App Identity API to authenticate to the Cloud Storage API and retrieve and list of all buckets in the project. **Note:** the [Google API Client Libraries](https://developers.google.com/discovery/libraries) can also manage much of this for you automatically. \n\n import json\n import logging\n\n from google.appengine.api import app_identity\n from google.appengine.api import urlfetch\n import webapp2\n\n\n class MainPage(webapp2.RequestHandler):\n def get(self):\n auth_token, _ = app_identity.get_access_token(\n \"https://www.googleapis.com/auth/cloud-platform\"\n )\n logging.info(\n \"Using token {} to represent identity {}\".format(\n auth_token, app_identity.get_service_account_name()\n )\n )\n\n response = urlfetch.fetch(\n \"https://www.googleapis.com/storage/v1/b?project={}\".format(\n app_identity.get_application_id()\n ),\n method=urlfetch.GET,\n headers={\"Authorization\": \"Bearer {}\".format(auth_token)},\n )\n\n if response.status_code != 200:\n raise Exception(\n \"Call failed. Status code {}. Body {}\".format(\n response.status_code, response.content\n )\n )\n\n result = json.loads(response.content)\n self.response.headers[\"Content-Type\"] = \"application/json\"\n self.response.write(json.dumps(result, indent=2))\n\n\n app = webapp2.WSGIApplication([(\"/\", MainPage)], debug=True)\n\nNote that the application's identity is represented by the service account name, which is typically *applicationid@appspot.gserviceaccount.com* . You can get the exact value by using the `get_service_account_name()` method.\nFor services which offer ACLs, you can grant the application access by granting this account access.\n\nAsserting identity to third-party services\n------------------------------------------\n\nThe token generated by `get_access_token()`\nonly works against Google services. However you can use the underlying signing technology to assert the identity of your application to other services. The `sign_blob()` method\nwill sign bytes using a private key unique to your application, and the `get_public_certificates()` method\nwill return certificates which can be used to validate the signature.\n| **Note:** The certificates may be rotated from time to time, and the method may return multiple certificates. Only certificates that are currently valid are returned; if you store signed messages you will need additional key management in order to verify signatures later.\nHere is an example showing how to sign a blob and validate its signature: \n\n\n import base64\n\n from Crypto.Hash import SHA256\n from Crypto.PublicKey import RSA\n from Crypto.Signature import PKCS1_v1_5\n from Crypto.Util.asn1 import DerSequence\n from google.appengine.api import app_identity\n import webapp2\n\n\n def verify_signature(data, signature, x509_certificate):\n \"\"\"Verifies a signature using the given x.509 public key certificate.\"\"\"\n\n # PyCrypto 2.6 doesn't support x.509 certificates directly, so we'll need\n # to extract the public key from it manually.\n # This code is based on https://github.com/google/oauth2client/blob/master\n # /oauth2client/_pycrypto_crypt.py\n pem_lines = x509_certificate.replace(b\" \", b\"\").split()\n cert_der = base64.urlsafe_b64decode(b\"\".join(pem_lines[1:-1]))\n cert_seq = DerSequence()\n cert_seq.decode(cert_der)\n tbs_seq = DerSequence()\n tbs_seq.decode(cert_seq[0])\n public_key = RSA.importKey(tbs_seq[6])\n\n signer = PKCS1_v1_5.new(public_key)\n digest = SHA256.new(data)\n\n return signer.verify(digest, signature)\n\n\n def verify_signed_by_app(data, signature):\n \"\"\"Checks the signature and data against all currently valid certificates\n for the application.\"\"\"\n public_certificates = app_identity.get_public_certificates()\n\n for cert in public_certificates:\n if verify_signature(data, signature, cert.x509_certificate_pem):\n return True\n\n return False\n\n\n class MainPage(webapp2.RequestHandler):\n def get(self):\n message = \"Hello, world!\"\n signing_key_name, signature = app_identity.sign_blob(message)\n verified = verify_signed_by_app(message, signature)\n\n self.response.content_type = \"text/plain\"\n self.response.write(\"Message: {}\\n\".format(message))\n self.response.write(\"Signature: {}\\n\".format(base64.b64encode(signature)))\n self.response.write(\"Verified: {}\\n\".format(verified))\n\n\n app = webapp2.WSGIApplication([(\"/\", MainPage)], debug=True)\n\nGetting the default Cloud Storage Bucket name\n---------------------------------------------\n\nEach application can have one default Cloud Storage bucket, which\nincludes\n[5GB of free storage and a free quota for I/O operations](/appengine/docs/quotas#Default_Gcs_Bucket).\n\nTo get the name of the default bucket,\n\nyou can use the App Identity API. Call\n[google.appengine.api.app_identity.app_identity.get_default_gcs_bucket_name](/appengine/docs/legacy/standard/python/refdocs/google.appengine.api.app_identity.app_identity)."]]