หลังจากที่ Google ส่งคืนโทเค็นรหัสแล้ว โทเค็นนี้จะส่งโดยคำขอเมธอด HTTP POST
ที่มีชื่อพารามิเตอร์ credential
ไปยังปลายทางการเข้าสู่ระบบ
ตัวอย่างในภาษา Python ที่แสดงขั้นตอนทั่วไปในการตรวจสอบและใช้โทเค็นรหัส มีดังนี้
ยืนยันโทเค็น Cross-Site Request Forgery (CSRF) เมื่อคุณส่งข้อมูลเข้าสู่ระบบไปยังปลายทางการเข้าสู่ระบบ เราจะใช้รูปแบบการส่งคุกกี้ซ้ำเพื่อป้องกันการโจมตี CSRF ก่อนที่จะส่งแต่ละครั้ง เราจะสร้างโทเค็น จากนั้น ระบบจะใส่โทเค็นลงในทั้งคุกกี้และส่วนเนื้อความของโพสต์ ดังที่แสดงในตัวอย่างโค้ดต่อไปนี้
csrf_token_cookie = self.request.cookies.get('g_csrf_token') if not csrf_token_cookie: webapp2.abort(400, 'No CSRF token in Cookie.') csrf_token_body = self.request.get('g_csrf_token') if not csrf_token_body: webapp2.abort(400, 'No CSRF token in post body.') if csrf_token_cookie != csrf_token_body: webapp2.abort(400, 'Failed to verify double submit cookie.')
ยืนยันโทเค็นรหัส
如需验证令牌是否有效,请确保满足以下条件:
- ID 令牌由 Google 正确签名。使用 Google 的公钥(以 JWK 或 PEM 格式提供)来验证令牌的签名。这些密钥会定期轮替;请检查响应中的
Cache-Control
标头,以确定何时应再次检索它们。 - ID 令牌中
aud
的值等于应用的某个客户端 ID。通过进行这项检查,可以防止向恶意应用颁发的 ID 令牌用于访问应用后端服务器上同一用户的数据。 - ID 令牌中
iss
的值等于accounts.google.com
或https://accounts.google.com
。 - ID 令牌的过期时间 (
exp
) 尚未过去。 - 如果您需要验证该 ID 令牌代表的是 Google Workspace 或 Cloud 组织帐号,则可以检查
hd
声明,该声明指示用户的托管网域。仅允许特定网域的成员访问资源时,必须使用此方法。缺少此声明表示帐号不属于 Google 托管的网域。
我们强烈建议您使用适合您的平台的 Google API 客户端库或通用 JWT 库,而不是自行编写代码来执行这些验证步骤。对于开发和调试,您可以调用我们的
tokeninfo
验证端点。使用 Google API 客户端库
在生产环境中验证 Google ID 令牌时,建议使用一个 Google API 客户端库(例如 Java、Node.js、PHP、Python)。
Java 要在 Java 中验证 ID 令牌,请使用 GoogleIdTokenVerifier 对象。例如:
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken; import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload; import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier; ... GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory) // Specify the CLIENT_ID of the app that accesses the backend: .setAudience(Collections.singletonList(CLIENT_ID)) // Or, if multiple clients access the backend: //.setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3)) .build(); // (Receive idTokenString by HTTPS POST) GoogleIdToken idToken = verifier.verify(idTokenString); if (idToken != null) { Payload payload = idToken.getPayload(); // Print user identifier String userId = payload.getSubject(); System.out.println("User ID: " + userId); // Get profile information from payload String email = payload.getEmail(); boolean emailVerified = Boolean.valueOf(payload.getEmailVerified()); String name = (String) payload.get("name"); String pictureUrl = (String) payload.get("picture"); String locale = (String) payload.get("locale"); String familyName = (String) payload.get("family_name"); String givenName = (String) payload.get("given_name"); // Use or store profile information // ... } else { System.out.println("Invalid ID token."); }
GoogleIdTokenVerifier.verify()
方法可验证 JWT 签名、aud
声明、iss
声明和exp
声明。如果您需要验证该 ID 令牌代表的是 Google Workspace 或 Cloud 组织帐号,则可以通过检查
Payload.getHostedDomain()
方法返回的域名来验证hd
声明。email
声明的网域不足以确保帐号由网域或组织管理。Node.js 如需在 Node.js 中验证 ID 令牌,请使用 Node.js 版 Google Auth 库。安装库:
npm install google-auth-library --save
然后,调用verifyIdToken()
函数。例如:const {OAuth2Client} = require('google-auth-library'); const client = new OAuth2Client(); async function verify() { const ticket = await client.verifyIdToken({ idToken: token, audience: CLIENT_ID, // Specify the CLIENT_ID of the app that accesses the backend // Or, if multiple clients access the backend: //[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3] }); const payload = ticket.getPayload(); const userid = payload['sub']; // If request specified a G Suite domain: // const domain = payload['hd']; } verify().catch(console.error);
verifyIdToken
函数会验证 JWT 签名、aud
声明、exp
声明和iss
声明。如果您需要验证该 ID 令牌代表的是 Google Workspace 或 Cloud 组织帐号,则可以检查
hd
声明,该声明指示用户的托管网域。仅允许特定网域的成员访问资源时,必须使用此方法。缺少此声明表示帐号不属于 Google 托管的网域。PHP 如需在 PHP 中验证 ID 令牌,请使用 PHP 版 Google API 客户端库。安装库(例如,使用 Composer):
composer require google/apiclient
然后,调用verifyIdToken()
函数。例如:require_once 'vendor/autoload.php'; // Get $id_token via HTTPS POST. $client = new Google_Client(['client_id' => $CLIENT_ID]); // Specify the CLIENT_ID of the app that accesses the backend $payload = $client->verifyIdToken($id_token); if ($payload) { $userid = $payload['sub']; // If request specified a G Suite domain: //$domain = $payload['hd']; } else { // Invalid ID token }
verifyIdToken
函数会验证 JWT 签名、aud
声明、exp
声明和iss
声明。如果您需要验证该 ID 令牌代表的是 Google Workspace 或 Cloud 组织帐号,则可以检查
hd
声明,该声明指示用户的托管网域。仅允许特定网域的成员访问资源时,必须使用此方法。缺少此声明表示帐号不属于 Google 托管的网域。Python 要在 Python 中验证 ID 令牌,请使用 verify_oauth2_token 函数。例如:
from google.oauth2 import id_token from google.auth.transport import requests # (Receive token by HTTPS POST) # ... try: # Specify the CLIENT_ID of the app that accesses the backend: idinfo = id_token.verify_oauth2_token(token, requests.Request(), CLIENT_ID) # Or, if multiple clients access the backend server: # idinfo = id_token.verify_oauth2_token(token, requests.Request()) # if idinfo['aud'] not in [CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]: # raise ValueError('Could not verify audience.') # If auth request is from a G Suite domain: # if idinfo['hd'] != GSUITE_DOMAIN_NAME: # raise ValueError('Wrong hosted domain.') # ID token is valid. Get the user's Google Account ID from the decoded token. userid = idinfo['sub'] except ValueError: # Invalid token pass
verify_oauth2_token
函数会验证 JWT 签名、aud
声明和exp
声明。您还必须通过检查verify_oauth2_token
返回的对象来验证hd
声明(如果适用)。如果多个客户端访问后端服务器,则还需要手动验证aud
声明。- ID 令牌由 Google 正确签名。使用 Google 的公钥(以 JWK 或 PEM 格式提供)来验证令牌的签名。这些密钥会定期轮替;请检查响应中的
เมื่อยืนยันความถูกต้องของโทเค็นแล้ว คุณจะใช้ข้อมูลในโทเค็นรหัส Google เพื่อเชื่อมโยงสถานะบัญชีของเว็บไซต์ได้ ดังนี้
ผู้ใช้ที่ไม่ได้ลงทะเบียน: คุณจะแสดงอินเทอร์เฟซผู้ใช้ (UI) สำหรับการลงชื่อสมัครใช้ที่อนุญาตให้ผู้ใช้ให้ข้อมูลโปรไฟล์เพิ่มเติมได้ หากจำเป็น นอกจากนี้ยังอนุญาตให้ผู้ใช้สร้างบัญชีใหม่และ เซสชันผู้ใช้ที่เข้าสู่ระบบโดยไม่ต้องแจ้งให้ทราบ
บัญชีที่มีอยู่แล้วในเว็บไซต์ของคุณ: คุณแสดงหน้าเว็บที่ให้ผู้ใช้ปลายทางป้อนรหัสผ่านและลิงก์บัญชีเดิมด้วยข้อมูลเข้าสู่ระบบของ Google ได้ เพื่อเป็นการยืนยันว่า ผู้ใช้มีสิทธิ์เข้าถึงบัญชีที่มีอยู่
ผู้ใช้ที่กลับมาแบบรวมศูนย์ที่กลับมา: คุณสามารถให้ผู้ใช้ลงชื่อเข้าใช้แบบเงียบ